import os
import sys
import signal
import argparse
import _fastpysgi

__version__ = '0.2'

LL_DISABLED    = 0
LL_FATAL_ERROR = 1
LL_CRIT_ERROR  = 2
LL_ERROR       = 3
LL_WARNING     = 4
LL_NOTICE      = 5
LL_INFO        = 6
LL_DEBUG       = 7
LL_TRACE       = 8

class _Server():
    def __init__(self):
        self.app = None
        self.host = "0.0.0.0"
        self.port = 5000
        self.backlog = 2048
        self.loglevel = LL_ERROR
        self.hook_sigint = 2            # 0 = ignore Ctrl-C; 1 = stop server on Ctrl-C; 2 = halt process on Ctrl-C
        self.allow_keepalive = True
        self.add_header_date = True
        self.add_header_server = "FastPySGI/{}".format(__version__)
        self.max_content_length = None  # def value: 999999999
        self.max_chunk_size = None      # def value: 256 KiB
        self.read_buffer_size = None    # def value: 64 KiB
        self.tcp_nodelay = 0            # 0 = Nagle's algo enabled; 1 = Nagle's algo disabled;
        self.tcp_keepalive = 0          # -1 = disabled; 0 = system default; 1...N = timeout in seconds
        self.tcp_send_buf_size = 0      # 0 = system default; 1...N = size in bytes
        self.tcp_recv_buf_size = 0      # 0 = system default; 1...N = size in bytes
        self.nowait = 0
        self.num_workers = 1
        self.worker_list = [ ]
        
    def init(self, app, host = None, port = None, loglevel = None, workers = None):
        self.app = app
        self.host = host if host else self.host
        self.port = port if port else self.port
        self.loglevel = loglevel if loglevel is not None else self.loglevel
        self.num_workers = workers if workers is not None else self.num_workers
        if self.num_workers > 1:
            return 0
        return _fastpysgi.init_server(self)

    def set_allow_keepalive(self, value):
        self.allow_keepalive = value
        _fastpysgi.change_setting(self, "allow_keepalive")

    def run(self):
        if self.nowait:
            if self.num_workers > 1:
                raise Exception('Incorrect server options')
            return _fastpysgi.run_nowait(self)
        if self.num_workers > 1:
            return self.multi_run()
        ret = _fastpysgi.run_server(self)
        self.close()
        return ret
        
    def close(self):
        return _fastpysgi.close_server(self)

    def multi_run(self, num_workers = None):
        if num_workers is not None:
            self.num_workers = num_workers
        for _ in range(self.num_workers):
            pid = os.fork()
            if pid > 0:
                self.worker_list.append(pid)
                print(f"Worker process added with PID: {pid}")
                continue
            try:
                _fastpysgi.init_server(self)
                _fastpysgi.run_server(self)
            except KeyboardInterrupt:
                pass
            sys.exit(0)
        try:
            for _ in range(self.num_workers):
                os.wait()
        except KeyboardInterrupt:
            print("\n" + "Stopping all workers")
            for worker in self.worker_list:
                os.kill(worker, signal.SIGINT)
        return 0

server = _Server()

# -------------------------------------------------------------------------------------

def import_from_string(import_str):
    import importlib
    import importlib.util
    module_str, _, attrs_str = import_str.partition(":")
    if not module_str or not attrs_str:
        raise ImportError("Import string should be in the format <module>:<attribute>")

    try:
        relpath = f"{module_str}.py"
        if os.path.isfile(relpath):
            spec = importlib.util.spec_from_file_location(module_str, relpath)
            module = importlib.util.module_from_spec(spec)
            spec.loader.exec_module(module)

        else:
            module = importlib.import_module(module_str)

        for attr_str in attrs_str.split("."):
            module = getattr(module, attr_str)
    except AttributeError:
        raise ImportError(f'Attribute "{attrs_str}" not found in module "{module_str}"')

    return module

# -------------------------------------------------------------------------------------

def run_from_cli():
    parser = argparse.ArgumentParser(
        prog="fastpysgi",
        description="Run FastPySGI server from CLI",
    )
    parser.add_argument(
        "--version",
        action="version",
        version=__version__,
    )
    parser.add_argument(
        "app_import_string",
        metavar="APP",
        type=str,
        help="WSGI/ASGI app in the format module:attribute (e.g. myapp:app)",
    )
    parser.add_argument(
        "--host",
        type=str,
        default=server.host,
        help=f"Host the socket is bound to (default: {server.host})",
    )
    parser.add_argument(
        "-p", "--port",
        type=int,
        default=server.port,
        help=f"Port the socket is bound to (default: {server.port})",
    )
    parser.add_argument(
        "-l", "--loglevel",
        type=int,
        default=server.loglevel,
        help=f"Logging level 0..8 (default: {server.loglevel})",
    )

    args = parser.parse_args()

    try:
        app = import_from_string(args.app_import_string)
    except ImportError as e:
        print(f"Error importing WSGI app: {e}", file=sys.stderr)
        sys.exit(1)

    server.init(app, args.host, args.port, args.loglevel)
    print(f"FastPySGI server listening at http://{server.host}:{server.port}")
    server.run()

# -------------------------------------------------------------------------------------

def run(app = None, host = None, port = None, loglevel = None, workers = None, wsgi_app = None):
    if app and wsgi_app:
        raise Exception("It is not allowed to specify several applications at once.")
    if app is None:
        app = wsgi_app
    if app is None:
        raise Exception("app not specify.")
    print("FastPySGI server running on PID:", os.getpid())
    server.init(app, host, port, loglevel, workers)
    addon = " multiple workers" if server.num_workers > 1 else ""
    print(f"FastPySGI server{addon} listening at http://{server.host}:{server.port}")
    server.run()
