# Copyright 2025 Softwell S.r.l.
# Licensed under the Apache License, Version 2.0

"""CLI entry point for genropy-asgi server.

Replicates the gnrwsgiserve startup flow (steps 1-6) identically:
config resolution, site path lookup, siteconfig loading, option merging.
Only the final step differs: uvicorn/ASGI instead of werkzeug/WSGI.

Usage:
    gnrasgiserve test_invoice_pg
    gnrasgiserve test_invoice_pg -p 8000
    gnrasgiserve test_invoice_pg --reload --debug
"""

from __future__ import annotations

import atexit
import logging
import os
import sys
import webbrowser

from gnr.core.cli import GnrCliArgParse
from gnr.core.gnrconfig import getGnrConfig, gnrConfigPath
from gnr.core.gnrdict import dictExtract
from gnr.app.pathresolver import PathResolver, EntityNotFoundException
from gnr.web.gnrwsgisite import GnrWsgiSite

log = logging.getLogger("genropy_asgi")

WSGI_OPTIONS = dict(
    port=8080,
    host="0.0.0.0",
    reload=False,
    debug=True,
    noclean=False,
    restore=False,
    source_instance=None,
    remote_edit=None,
)


class Server:
    """ASGI server for GenroPy sites.

    Mirrors gnr.web.serverwsgi.Server steps 1-6 (argument parsing, config
    resolution, site path lookup, siteconfig loading, option merging) and
    replaces step 7 with uvicorn serving the GnrWsgiSite through the
    WsgiGateway middleware.
    """

    description = "Start ASGI development server for a GenroPy site."

    def __init__(self, site_script: str | None = None) -> None:
        parser = GnrCliArgParse(description=self.description)

        parser.add_argument("--reload", dest="reload",
                            action="store_true",
                            help="Use auto-restart file monitor")
        parser.add_argument("--noreload", dest="reload",
                            action="store_false",
                            help="Do not use auto-restart file monitor")
        parser.add_argument("--nodebug", dest="nodebug",
                            action="store_true",
                            help="Disable debug mode")
        parser.add_argument("-o", "--open", dest="open_browser",
                            action="store_true",
                            help="Open browser automatically")
        parser.add_argument("-c", "--config", dest="config_path",
                            help="gnrserve directory path")
        parser.add_argument("-p", "--port", dest="port",
                            help="Server listening port (default: 8080)")
        parser.add_argument("-H", "--host", dest="host",
                            help="Server listening address (default: 0.0.0.0)")
        parser.add_argument("--restore", dest="restore",
                            help="Restore from path")
        parser.add_argument("--source_instance", dest="source_instance",
                            help="Import from instance")
        parser.add_argument("--remote_edit", dest="remote_edit",
                            action="store_true",
                            help="Enable remote edit")
        parser.add_argument("--remotedb", nargs="?", const=True,
                            default=None, dest="remotedb",
                            help="Use a remote db")
        parser.add_argument("site_name", nargs="?")
        parser.add_argument("-s", "--site", dest="site_name_opt",
                            help="Site identified by supplied name")
        parser.add_argument("-n", "--noclean", dest="noclean",
                            action="store_true",
                            help="Don't perform a clean restart")
        parser.add_argument("--counter", dest="counter",
                            help="Startup counter")
        parser.add_argument("--ssl_cert", dest="ssl_cert",
                            help="SSL cert")
        parser.add_argument("--ssl_key", dest="ssl_key",
                            help="SSL key")
        parser.add_argument("--ssl", dest="ssl", action="store_true",
                            help="SSL")

        self.site_script = site_script
        self.app_scheme = "http"
        self.app_host = "127.0.0.1"
        self.app_port = "8080"

        parser.set_defaults(loglevel="info")
        self.options = parser.parse_args()

        if hasattr(self.options, "config_path") and self.options.config_path:
            self.config_path = self.options.config_path
        else:
            self.config_path = gnrConfigPath()
        self.gnr_config = getGnrConfig(
            config_path=self.config_path, set_environment=True
        )

        self.site_name = (
            self.options.site_name_opt
            or self.options.site_name
            or os.getenv("GNR_CURRENT_SITE")
        )
        if not self.site_name and not self.site_script:
            log.error("site name is required")
            sys.exit(1)

        if not self.site_name:
            self.site_name = os.path.basename(
                os.path.dirname(site_script)  # type: ignore[arg-type]
            )

        # --remotedb handling (identical to serverwsgi.py)
        if self.options.remotedb:
            if self.options.remotedb is True:
                self.site_name = f"{self.site_name}:{self.site_name}"
            else:
                self.site_name = f"{self.site_name}:{self.options.remotedb}"

        self.remote_db = ""
        if self.site_name and ":" in self.site_name:
            self.site_name, self.remote_db = self.site_name.split(":", 1)

        if not self.gnr_config:
            raise RuntimeError("Error: no ~/.gnr/ or /etc/gnr/ found")

        self.site_path = PathResolver().site_name_to_path(self.site_name)
        self.site_script = os.path.join(self.site_path, "root.py")
        if not os.path.isfile(self.site_script):
            self.site_script = os.path.join(self.site_path, "..", "root.py")
            if not os.path.exists(self.site_script):
                raise RuntimeError(
                    f"Error: no root.py in the site provided ({self.site_name})"
                )

        self.init_options()

    def init_options(self) -> None:
        """Merge CLI options with env vars and siteconfig (same as serverwsgi)."""
        self.siteconfig = PathResolver().get_siteconfig(self.site_name)
        options = self.options.__dict__
        envopt = dictExtract(os.environ, "GNR_WSGI_OPT_")
        for option in list(options.keys()):
            if not options.get(option, None):
                option_value = WSGI_OPTIONS.get(option)
                site_option_value = self.siteconfig[f"wsgi?{option}"]
                if site_option_value is not None:
                    option_value = site_option_value
                if option in envopt:
                    option_value = envopt.get(option)
                self.options.__dict__[option] = option_value

    @property
    def app_url(self) -> str:
        return f"{self.app_scheme}://{self.app_host}:{self.app_port}"

    def run(self) -> None:
        """Determine flags and call serve()."""
        self.reloader = not (
            self.options.reload == "false"
            or self.options.reload is False
            or self.options.reload is None
        )
        self.debug = not (
            self.options.debug == "false"
            or self.options.debug is False
            or self.options.debug is None
            or self.options.nodebug
        )
        self.serve()

    def serve(self) -> None:
        """Create GnrWsgiSite and serve via uvicorn through ASGI gateway."""
        self.app_port = int(self.options.port)
        self.app_host = self.options.host
        site_name = (
            f"{self.site_name}:{self.remote_db}"
            if self.remote_db
            else self.site_name
        )

        # --- Steps 1-6: identical to serverwsgi.py ---
        gnr_site = GnrWsgiSite(
            self.site_script,
            site_name=site_name,
            _config=self.siteconfig,
            _gnrconfig=self.gnr_config,
            counter=getattr(self.options, "counter", None),
            noclean=self.options.noclean,
            options=self.options,
        )
        gnr_site._local_mode = True
        # GenroPy site is mounted at root — default_uri stays "/"
        atexit.register(gnr_site.on_site_stop)

        log.info(
            "Starting ASGI server — listening on %s (debug=%s, reload=%s)",
            self.app_url, self.debug, self.reloader,
        )

        if self.options.open_browser:
            webbrowser.open(f"{self.app_url}/index")

        # --- Step 7: ASGI instead of werkzeug ---
        from genro_asgi import AsgiServer

        server = AsgiServer(host=str(self.app_host), port=int(self.app_port),
                            reload=False)
        server.wsgi_app = gnr_site
        server.run()


def main() -> int:
    """Entry point for gnrasgiserve command."""
    try:
        server = Server()
        server.run()
    except EntityNotFoundException as e:
        print(e)
        return 1
    except KeyboardInterrupt:
        print("\nShutdown.")
    return 0


if __name__ == "__main__":
    sys.exit(main())
