"""Frontend-serving endpoints: dashboard UI, JSX, JS, config — extracted from main.py."""

import os
import re

import structlog
from fastapi import APIRouter, Request
from fastapi.responses import Response

from proxym.config import get_settings

logger = structlog.get_logger()

router = APIRouter()

_HERE = os.path.dirname(os.path.abspath(__file__))
_SRC_ROOT = os.path.dirname(_HERE)  # src/proxym/
_MIME_JS = "text/javascript"
_MIME_JS_UTF8 = "text/javascript; charset=utf-8"
_FILE_NOT_FOUND_BODY = "// file not found"


def _serve_file(paths: list[str], media_type: str, charset_header: str | None = None) -> Response:
    """Try paths in order; return content from first found file."""
    for file_path in paths:
        try:
            with open(file_path, "r") as f:
                content = f.read()
            headers = {"Content-Type": charset_header} if charset_header else {}
            return Response(content=content, media_type=media_type, headers=headers)
        except FileNotFoundError:
            continue
        except Exception as e:
            logger.error("serve_file_error", error=str(e), path=file_path)
            continue
    logger.error("serve_file_not_found", paths=paths)
    return Response(content=_FILE_NOT_FOUND_BODY, media_type=media_type, status_code=404)


def _serve_concat_files(paths: list[str], media_type: str, charset_header: str | None = None) -> Response:
    parts: list[str] = []
    missing: list[str] = []
    for file_path in paths:
        try:
            with open(file_path, "r") as f:
                parts.append(f.read())
        except FileNotFoundError:
            missing.append(file_path)
        except Exception as e:
            logger.error("serve_file_error", error=str(e), path=file_path)
            missing.append(file_path)

    if missing:
        logger.error("serve_concat_files_not_found", paths=missing)
        return Response(content=_FILE_NOT_FOUND_BODY, media_type=media_type, status_code=404)

    content = "\n\n".join(parts) + "\n"
    headers = {"Content-Type": charset_header} if charset_header else {}
    return Response(content=content, media_type=media_type, headers=headers)


def _strip_module_syntax(content: str) -> str:
    """Remove ES module syntax so Babel-standalone can execute the file as a global script."""
    lines: list[str] = []
    for line in content.splitlines():
        if re.match(r"^\s*import\s+", line):
            continue
        if re.match(r"^\s*export\s+default\b", line):
            continue
        if re.match(r"^\s*export\s*\{", line):
            continue
        lines.append(line)
    return "\n".join(lines) + "\n"


def _serve_global_script(paths: list[str], media_type: str, charset_header: str | None = None) -> Response:
    """Serve a JSX/JS file after stripping module syntax for the global-script loader."""
    for file_path in paths:
        try:
            with open(file_path, "r") as f:
                content = _strip_module_syntax(f.read())
            headers = {"Content-Type": charset_header} if charset_header else {}
            return Response(content=content, media_type=media_type, headers=headers)
        except FileNotFoundError:
            continue
        except Exception as e:
            logger.error("serve_global_script_error", error=str(e), path=file_path)
            continue
    logger.error("serve_global_script_not_found", paths=paths)
    return Response(content=_FILE_NOT_FOUND_BODY, media_type=media_type, status_code=404)


@router.get("/")
async def root_redirect():
    """Redirect root to dashboard."""
    from fastapi.responses import RedirectResponse
    return RedirectResponse(url="/dashboard-ui")


@router.get("/dashboard-ui")
async def dashboard_ui():
    """Serve the React dashboard UI."""
    paths = [
        "frontend/dashboard.html",
        "/app/frontend/dashboard.html",
    ]
    resp = _serve_file(paths, "text/html", "text/html; charset=utf-8")
    if resp.status_code == 404:
        return Response(
            content="<h1>Dashboard Error</h1><p>Could not load dashboard: HTML file not found</p>",
            status_code=500,
            media_type="text/html",
        )
    return resp


# ── Component map: one entry per JSX/JS endpoint ─────────────

_DASHBOARD_DIR = os.path.join(_SRC_ROOT, "dashboard")

_COMPONENT_MAP: dict[str, str] = {
    "sse-service":        "../../../frontend/dashboard-sse-service.js",
    "hook":               "hooks/useDashboardData.js",
    "api":                "api.js",
    "formatters":         "utils/formatters.js",
    "ui-components":      "components/ui.jsx",
    "models":             "components/models.jsx",
    "accounts":           "components/accounts.jsx",
    "users":              "components/users.jsx",
    "layout":             "components/layout.jsx",
    "overview":           "components/overview.jsx",
    "vms":                "components/vms.jsx",
    "logs":               "components/logs.jsx",
    "voice-schemas":      "components/voice_schemas.jsx",
    "voice-engine":       "components/voice_engine.jsx",
    "voice":              "components/voice.jsx",
    "projects":           "components/projects.jsx",
    "diag-helpers":       "components/diagnostics/helpers.jsx",
    "diag-infrastructure":"components/diagnostics/DiagInfrastructure.jsx",
    "diag-config":        "components/diagnostics/DiagConfig.jsx",
    "diag-extensions":    "components/diagnostics/DiagExtensions.jsx",
    "diag-agent":         "components/diagnostics/DiagAgent.jsx",
    "tools":              "components/tools.jsx",
    "tickets":            "components/tickets.jsx",
    "human":              "components/human.jsx",
    "environments":       "components/environments.jsx",
    "home":               "components/home.jsx",
    "tasks":              "components/tasks.jsx",
    "integrations":       "components/integrations.jsx",
    "system":             "components/system.jsx",
    "setup":              "components/setup.jsx",
}


def _register_component_routes(rtr: APIRouter, base_dir: str) -> None:
    """Register all JSX/JS serving endpoints from _COMPONENT_MAP."""
    for slug, rel_path in _COMPONENT_MAP.items():
        full_path = os.path.join(base_dir, rel_path)
        ext = ".js" if rel_path.endswith(".js") else ".jsx"
        route = f"/dashboard-{slug}{ext}"

        # Closure with default arg to avoid late binding
        def _make_handler(_fp: str = full_path):
            def _handler():
                return _serve_file([_fp], _MIME_JS, _MIME_JS_UTF8)
            return _handler

        rtr.add_api_route(route, _make_handler(), methods=["GET"])


def _register_component_tree_routes(rtr: APIRouter, base_dir: str) -> None:
    """Register nested refactored dashboard components as global scripts."""
    components_dir = os.path.join(base_dir, "components")
    for tree_root in ("shared", "tasks", "tickets", "tools", "human"):
        tree_dir = os.path.join(components_dir, tree_root)
        if not os.path.isdir(tree_dir):
            continue

        for root, _, files in os.walk(tree_dir):
            for fname in sorted(files):
                if not fname.endswith(".jsx"):
                    continue

                full_path = os.path.join(root, fname)
                rel_path = os.path.relpath(full_path, components_dir)
                slug = rel_path[:-4].replace(os.sep, "-")
                route = f"/dashboard-{slug}.jsx"

                def _make_handler(_fp: str = full_path):
                    def _handler():
                        return _serve_global_script([_fp], _MIME_JS, _MIME_JS_UTF8)

                    return _handler

                rtr.add_api_route(route, _make_handler(), methods=["GET"])


_register_component_routes(router, _DASHBOARD_DIR)
_register_component_tree_routes(router, _DASHBOARD_DIR)


# ── Special endpoints with unique logic ──────────────────────

@router.get("/dashboard-diagnostics.jsx")
async def dashboard_diagnostics_jsx():
    """Serve DiagnosticsPanel for testing VS Code, proxy, and agent."""
    base = os.path.join(_DASHBOARD_DIR, "components")
    return _serve_concat_files(
        [
            os.path.join(base, "diagnostics", "helpers.jsx"),
            os.path.join(base, "diagnostics", "DiagInfrastructure.jsx"),
            os.path.join(base, "diagnostics", "DiagConfig.jsx"),
            os.path.join(base, "diagnostics", "DiagExtensions.jsx"),
            os.path.join(base, "diagnostics", "DiagAgent.jsx"),
            os.path.join(base, "diagnostics.jsx"),
        ],
        _MIME_JS,
        _MIME_JS_UTF8,
    )


@router.get("/proxym-dashboard.jsx")
async def dashboard_jsx():
    """Serve the React dashboard JSX entry point."""
    return _serve_file(
        [
            os.path.join(_DASHBOARD_DIR, "panel.jsx"),
            "frontend/proxym-dashboard.jsx",
            "/app/frontend/proxym-dashboard.jsx",
        ],
        _MIME_JS, _MIME_JS_UTF8,
    )


@router.get("/config")
async def get_frontend_config(request: Request):
    """Return configuration for frontend."""
    settings = get_settings()
    
    # Use Host header to get the actual port
    host_header = request.headers.get("host", "localhost:4000")
    if ":" in host_header:
        host, port = host_header.split(":")
        scheme = request.url.scheme
    else:
        host = host_header
        port = settings.port
        scheme = "http"
    
    api_url = f"{scheme}://{host}:{port}"
    
    return {
        "api_url": api_url,
        "api_key": settings.master_key,
        "debug": settings.debug,
        "dashboard_api_url": settings.dashboard_api_url,
        "default_proxy_url": settings.default_proxy_url,
        "monthly_budget_usd": settings.monthly_budget_usd,
        "daily_budget_usd": settings.daily_budget_usd,
        "max_request_cost_usd": settings.max_request_cost_usd,
        "available_providers": list(settings.available_providers().keys()),
        "redis_url": settings.redis_url,
        "ollama_base_url": settings.ollama_base_url,
        "watch_dir": settings.watch_dir,
        "max_context_tokens": settings.max_context_tokens,
        "delta_threshold": settings.delta_threshold,
    }
