import importlib
import os
import sys
import subprocess
import argparse
import signal
import tempfile
import time
import requests

import src.commons.utils as utils


logger = utils.get_model_server_logger()


def get_version():
    try:
        version = importlib.metadata.version("archgw_modelserver")
        return version
    except importlib.metadata.PackageNotFoundError:
        return "version not found"


def wait_for_health_check(url, timeout=300):
    """Wait for the Uvicorn server to respond to health-check requests."""

    start_time = time.time()
    while time.time() - start_time < timeout:
        try:
            response = requests.get(url)
            if response.status_code == 200:
                return True
        except requests.ConnectionError:
            time.sleep(1)

    return False


def get_pid_file():
    temp_dir = tempfile.gettempdir()
    return os.path.join(temp_dir, "model_server.pid")


def ensure_killed(process):
    process.terminate()
    # if the process is not terminated, kill it
    now = time.time()
    # wait for 5 seconds
    while time.time() - now < 5:
        if process.poll() is not None:
            break
        time.sleep(1)
    if process.poll() is None:
        logger.info("Killing model server")
        process.kill()


def start_server(port=51000, foreground=False):
    """Start the Uvicorn server."""

    logger.info("model server version: %s", get_version())

    stop_server()

    logger.info(
        "starting model server, port: %s, foreground: %s. Please wait ...",
        port,
        foreground,
    )

    if foreground:
        process = subprocess.Popen(
            [
                sys.executable,
                "-m",
                "uvicorn",
                "src.main:app",
                "--host",
                "0.0.0.0",
                "--port",
                str(port),
            ],
        )
    else:
        process = subprocess.Popen(
            [
                sys.executable,
                "-m",
                "uvicorn",
                "src.main:app",
                "--host",
                "0.0.0.0",
                "--port",
                str(port),
            ],
            stderr=subprocess.PIPE,
            stdout=subprocess.PIPE,
        )

    try:
        if wait_for_health_check(f"http://0.0.0.0:{port}/healthz"):
            logger.info(
                f"model server health check passed, port {port}, pid: {process.pid}"
            )
        else:
            logger.error("health check failed, shutting it down.")
            process.terminate()
    except KeyboardInterrupt:
        logger.info("model server stopped by user during initialization.")
        ensure_killed(process)

    # write process id to temp file in temp folder
    pid_file = get_pid_file()
    logger.info(f"writing pid {process.pid} to {pid_file}")
    with open(pid_file, "w") as f:
        f.write(str(process.pid))

    if foreground:
        try:
            process.wait()
        except KeyboardInterrupt:
            logger.info("model server stopped by user.")
            ensure_killed(process)


def stop_server():
    """Stop the Uvicorn server."""

    pid_file = get_pid_file()
    if os.path.exists(pid_file):
        logger.info("PID file found, shutting down the server.")
        # read pid from file
        with open(pid_file, "r") as f:
            pid = int(f.read())
            logger.info(f"Killing model server {pid}")
            try:
                os.kill(pid, signal.SIGKILL)
            except ProcessLookupError:
                logger.info(f"Process {pid} not found")
        os.remove(pid_file)
    else:
        logger.info("No PID file found, server is not running.")


def restart_server(port=51000, foreground=False):
    """Restart the Uvicorn server."""
    stop_server()
    start_server(port, foreground)


def parse_args():
    parser = argparse.ArgumentParser(description="Manage the Uvicorn server.")
    parser.add_argument(
        "action",
        choices=["start", "stop", "restart"],
        default="start",
        nargs="?",
        help="Action to perform on the server (default: start).",
    )
    parser.add_argument(
        "--port",
        type=int,
        default=51000,
        help="Port number for the server (default: 51000).",
    )

    parser.add_argument(
        "--foreground",
        default=False,
        action="store_true",
        help="Run the server in the foreground (default: False).",
    )

    return parser.parse_args()


def main():
    """
    Start, stop, or restart the Uvicorn server based on command-line arguments.
    """

    args = parse_args()

    if args.action == "start":
        logger.info("[CLI] - Starting server")
        start_server(args.port, args.foreground)
    elif args.action == "stop":
        logger.info("[CLI] - Stopping server")
        stop_server()
    elif args.action == "restart":
        logger.info("[CLI] - Restarting server")
        restart_server(args.port)
    else:
        logger.error(f"[CLI] - Unknown action: {args.action}")
        sys.exit(1)
