from __future__ import annotations

import importlib
import json
from pathlib import Path
from types import SimpleNamespace
from urllib.parse import quote

import api.config as api_config
import api.routes as routes



ROOT = Path(__file__).resolve().parent.parent


class _FakeHandler:
    def __init__(self, request_headers=None):
        self.status = None
        self.sent_headers = []
        self.body = bytearray()
        self.headers = dict(request_headers or {})
        self.wfile = self

    def send_response(self, status):
        self.status = status

    def send_header(self, name, value):
        self.sent_headers.append((name, value))

    def end_headers(self):
        pass

    def write(self, data):
        self.body.extend(data)

    def header(self, name):
        for key, value in self.sent_headers:
            if key.lower() == name.lower():
                return value
        return None


def _get(path, request_headers=None):
    handler = _FakeHandler(request_headers)
    routes.handle_get(handler, SimpleNamespace(path=path, query=""))
    return handler


def test_config_owner_returns_checkout_static_root():
    static_root = ROOT / "static"
    assert api_config.get_static_root() == static_root
    assert api_config.get_index_html_path() == static_root / "index.html"


def test_manifest_routes_follow_selected_static_root(tmp_path, monkeypatch):
    static_root = tmp_path / "static"
    static_root.mkdir()
    manifest_path = static_root / "manifest.json"
    payload = json.dumps({"name": "temp", "display": "standalone"}).encode("utf-8")
    manifest_path.write_bytes(payload)
    monkeypatch.setattr(api_config, "get_static_root", lambda: static_root)

    handler = _get("/manifest.json")
    assert handler.status == 200
    assert handler.header("Content-Type") == "application/manifest+json; charset=utf-8"
    assert handler.header("Cache-Control") == "no-store"
    assert bytes(handler.body) == payload

    session_handler = _get("/session/manifest.webmanifest")
    assert session_handler.status == 200
    assert bytes(session_handler.body) == payload


def test_service_worker_and_favicon_follow_selected_static_root(tmp_path, monkeypatch):
    static_root = tmp_path / "static"
    static_root.mkdir()
    sw_path = static_root / "sw.js"
    sw_path.write_text("const version = '__WEBUI_VERSION__';\n", encoding="utf-8")
    favicon_path = static_root / "favicon.ico"
    favicon_path.write_bytes(b"favicon-bytes")
    monkeypatch.setattr(api_config, "get_static_root", lambda: static_root)

    sw_handler = _get("/sw.js")
    # The route resolves api.updates dynamically on each request. Other update
    # tests deliberately evict and re-import that module, so a collection-time
    # alias can become stale even though the checkout and runtime module agree.
    webui_version = importlib.import_module("api.updates").WEBUI_VERSION
    expected = sw_path.read_text(encoding="utf-8").replace(
        "__WEBUI_VERSION__", quote(webui_version, safe="")
    ).encode("utf-8")
    assert sw_handler.status == 200
    assert sw_handler.header("Service-Worker-Allowed") == "/"
    assert sw_handler.header("Cache-Control") == "no-store"
    assert bytes(sw_handler.body) == expected

    favicon_handler = _get("/favicon.ico")
    assert favicon_handler.status == 200
    assert favicon_handler.header("Content-Type") == "image/x-icon"
    assert bytes(favicon_handler.body) == b"favicon-bytes"

    favicon_path.unlink()
    missing_favicon_handler = _get("/favicon.ico")
    assert missing_favicon_handler.status == 204


def test_index_shell_and_static_route_use_selected_root(tmp_path, monkeypatch):
    static_root = tmp_path / "static"
    static_root.mkdir()

    index_path = static_root / "index.html"
    index_path.write_bytes(
        b"<html>__WEBUI_VERSION__ __MAX_UPLOAD_BYTES__ __CSRF_TOKEN_JSON__ temp</html>"
    )
    ui_path = static_root / "ui.js"
    ui_path.write_bytes(b"console.log('temp static');\n")

    monkeypatch.setattr(api_config, "get_static_root", lambda: static_root)
    monkeypatch.setattr(api_config, "get_index_html_path", lambda: index_path)
    monkeypatch.setattr(routes, "_INDEX_SHELL_CACHE", {})
    monkeypatch.setattr(routes, "_STATIC_CACHE", {})

    shell = routes._render_index_shell_base()
    assert "temp" in shell
    assert "__WEBUI_VERSION__" not in shell
    assert "__MAX_UPLOAD_BYTES__" not in shell
    assert "__CSRF_TOKEN_JSON__" in shell

    temp_static = _get("/static/ui.js")
    assert temp_static.status == 200
    assert bytes(temp_static.body) == b"console.log('temp static');\n"
    assert bytes(temp_static.body) != (ROOT / "static" / "ui.js").read_bytes()

    traversal = _get("/static/../api/routes.py")
    assert traversal.status == 404
