"""
Hermes Web UI -- Main server entry point.
Thin routing shell: imports Handler, delegates to api/routes.py, runs server.
All business logic lives in api/*.
"""
import atexit
import faulthandler
import os
import signal
import sys
import time
import traceback
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from urllib.parse import urlparse

from api.auth import check_auth
from api.config import HOST, PORT, STATE_DIR, SESSION_DIR, DEFAULT_WORKSPACE
from api.helpers import j
from api.routes import handle_get, handle_post


class Handler(BaseHTTPRequestHandler):
    server_version = 'HermesWebUI/0.2'
    def log_message(self, fmt, *args): pass  # suppress default Apache-style log

    def log_request(self, code: str='-', size: str='-') -> None:
        """Structured JSON logs for each request."""
        import json as _json
        duration_ms = round((time.time() - getattr(self, '_req_t0', time.time())) * 1000, 1)
        record = _json.dumps({
            'ts': time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()),
            'method': self.command or '-',
            'path': self.path or '-',
            'status': int(code) if str(code).isdigit() else code,
            'ms': duration_ms,
        })
        print(f'[webui] {record}', flush=True)

    def do_GET(self) -> None:
        self._req_t0 = time.time()
        try:
            parsed = urlparse(self.path)
            if not check_auth(self, parsed): return
            result = handle_get(self, parsed)
            if result is False:
                return j(self, {'error': 'not found'}, status=404)
        except Exception as e:
            print(f'[webui] ERROR {self.command} {self.path}\n' + traceback.format_exc(), flush=True)
            return j(self, {'error': 'Internal server error'}, status=500)

    def do_POST(self) -> None:
        self._req_t0 = time.time()
        try:
            parsed = urlparse(self.path)
            if not check_auth(self, parsed): return
            result = handle_post(self, parsed)
            if result is False:
                return j(self, {'error': 'not found'}, status=404)
        except Exception as e:
            print(f'[webui] ERROR {self.command} {self.path}\n' + traceback.format_exc(), flush=True)
            return j(self, {'error': 'Internal server error'}, status=500)


def _install_exit_diagnostics() -> None:
    def _log(msg: str) -> None:
        print(f'[webui] LIFECYCLE {msg}', flush=True)

    try:
        faulthandler.enable(file=sys.stderr, all_threads=True)
    except Exception:
        pass

    def _dump_stacks(reason: str) -> None:
        _log(f'stack dump start reason={reason} pid={os.getpid()} ppid={os.getppid()}')
        try:
            faulthandler.dump_traceback(file=sys.stderr, all_threads=True)
        except Exception as exc:
            _log(f'stack dump failed: {exc}')
        _log(f'stack dump end reason={reason}')

    def _signal_handler(signum, frame):
        try:
            signame = signal.Signals(signum).name
        except Exception:
            signame = str(signum)
        _log(f'received signal {signame} pid={os.getpid()} ppid={os.getppid()}')
        _dump_stacks(f'signal:{signame}')
        signal.signal(signum, signal.SIG_DFL)
        os.kill(os.getpid(), signum)

    atexit.register(lambda: _log(f'atexit pid={os.getpid()} ppid={os.getppid()}'))

    for sig in (getattr(signal, 'SIGTERM', None), getattr(signal, 'SIGHUP', None), getattr(signal, 'SIGINT', None), getattr(signal, 'SIGUSR1', None)):
        if sig is None:
            continue
        try:
            signal.signal(sig, _signal_handler)
        except Exception:
            pass


def main() -> None:
    from api.config import print_startup_config, verify_hermes_imports, _HERMES_FOUND

    _install_exit_diagnostics()
    print(f'[webui] LIFECYCLE startup pid={os.getpid()} ppid={os.getppid()} argv={sys.argv}', flush=True)
    print_startup_config()

    ok, missing, errors = verify_hermes_imports()
    if not ok and _HERMES_FOUND:
        print(f'[!!] Warning: Hermes agent found but missing modules: {missing}', flush=True)
        for mod, err in errors.items():
            print(f'     {mod}: {err}', flush=True)
        print('     Agent features may not work correctly.', flush=True)

    STATE_DIR.mkdir(parents=True, exist_ok=True)
    SESSION_DIR.mkdir(parents=True, exist_ok=True)
    DEFAULT_WORKSPACE.mkdir(parents=True, exist_ok=True)
    httpd = ThreadingHTTPServer((HOST, PORT), Handler)
    print(f'  Hermes Web UI listening on http://{HOST}:{PORT}', flush=True)
    if HOST == '127.0.0.1':
        print(f'  Remote access: ssh -N -L {PORT}:127.0.0.1:{PORT} <user>@<your-server>', flush=True)
    print(f'  Then open:     http://localhost:{PORT}', flush=True)
    print('', flush=True)
    try:
        httpd.serve_forever()
        print('[webui] LIFECYCLE serve_forever returned normally', flush=True)
    finally:
        print('[webui] LIFECYCLE main() exiting', flush=True)

if __name__ == '__main__':
    main()
