from __future__ import annotations
import asyncio
import json
import re
import time
from dataclasses import dataclass
from pathlib import Path

import httpx

CACHE_DIR = Path.cwd() / "databases" / "hsts_preload_cache"
CACHE_TTL = 24 * 60 * 60

CHROME_URL = "https://chromium.googlesource.com/chromium/src/+/refs/heads/main/net/http/transport_security_state_static.json?format=TEXT"
FIREFOX_URL = "https://hg.mozilla.org/mozilla-central/raw-file/tip/security/manager/ssl/nsSTSPreloadList.inc"

PRELOAD_BROWSERS = ["chrome", "firefox", "edge", "ie"]


@dataclass
class PreloadResult:
    browser: str
    listed: bool
    include_subdomains: bool = False
    source_error: str | None = None


def _cache_path(name: str) -> Path:
    CACHE_DIR.mkdir(parents=True, exist_ok=True)
    return CACHE_DIR / f"{name}.json"


def _cache_fresh(p: Path) -> bool:
    try:
        return p.is_file() and (time.time() - p.stat().st_mtime) < CACHE_TTL
    except Exception:
        return False


async def _fetch_chrome_list(timeout: float = 30.0) -> dict[str, bool]:
    """Return mapping: host -> include_subdomains.

    Chromium's list is served base64-encoded when ?format=TEXT.
    """
    cache = _cache_path("chrome")
    if _cache_fresh(cache):
        try:
            return json.loads(cache.read_text())
        except Exception:
            pass
    try:
        async with httpx.AsyncClient(timeout=timeout, headers={"User-Agent": "nercone-tls-test/1.0"}) as client:
            resp = await client.get(CHROME_URL)
        if resp.status_code != 200:
            return {}
        import base64
        raw = base64.b64decode(resp.content).decode("utf-8", errors="replace")
    except Exception:
        return {}
    # Strip // comments (json with comments) and parse a loose subset
    cleaned = re.sub(r"//[^\n]*", "", raw)
    cleaned = re.sub(r"/\*.*?\*/", "", cleaned, flags=re.DOTALL)
    result: dict[str, bool] = {}
    try:
        data = json.loads(cleaned)
        for entry in data.get("entries", []):
            name = entry.get("name")
            if name:
                result[name.lower()] = bool(entry.get("include_subdomains", False))
    except Exception:
        for m in re.finditer(r'"name"\s*:\s*"([^"]+)"\s*,\s*"policy"[^}]*?"include_subdomains"\s*:\s*(true|false)', cleaned):
            result[m.group(1).lower()] = m.group(2) == "true"
    if result:
        try:
            cache.write_text(json.dumps(result))
        except Exception:
            pass
    return result


async def _fetch_firefox_list(timeout: float = 30.0) -> dict[str, bool]:
    cache = _cache_path("firefox")
    if _cache_fresh(cache):
        try:
            return json.loads(cache.read_text())
        except Exception:
            pass
    try:
        async with httpx.AsyncClient(timeout=timeout, headers={"User-Agent": "nercone-tls-test/1.0"}) as client:
            resp = await client.get(FIREFOX_URL)
        if resp.status_code != 200:
            return {}
        text = resp.text
    except Exception:
        return {}
    result: dict[str, bool] = {}
    # Format: lines like %%\nhost, include_subdomains_bool\n%%
    for line in text.splitlines():
        line = line.strip()
        if not line or line.startswith("/") or line.startswith("%") or line.startswith("#"):
            continue
        if "," in line:
            host, flag = [p.strip() for p in line.split(",", 1)]
            result[host.lower()] = "1" in flag or "true" in flag.lower()
    if result:
        try:
            cache.write_text(json.dumps(result))
        except Exception:
            pass
    return result


def _lookup(host: str, table: dict[str, bool]) -> tuple[bool, bool]:
    h = host.lower().strip(".")
    if h in table:
        return True, table[h]
    # walk parent domains to catch include_subdomains matches
    parts = h.split(".")
    for i in range(1, len(parts)):
        parent = ".".join(parts[i:])
        if parent in table and table[parent]:
            return True, True
    return False, False


async def check_preload(host: str) -> list[PreloadResult]:
    chrome = await _fetch_chrome_list()
    firefox = await _fetch_firefox_list()
    results: list[PreloadResult] = []
    c_listed, c_sub = _lookup(host, chrome)
    f_listed, f_sub = _lookup(host, firefox)
    results.append(PreloadResult("chrome", c_listed, c_sub, None if chrome else "source unavailable"))
    results.append(PreloadResult("firefox", f_listed, f_sub, None if firefox else "source unavailable"))
    # Edge effectively uses Chromium's preload list since the Edge (Chromium) release.
    results.append(PreloadResult("edge", c_listed, c_sub, None if chrome else "source unavailable"))
    # Internet Explorer never maintained its own HSTS preload list; treat same as listed-in-none.
    results.append(PreloadResult("ie", False, False, "IE does not support HSTS preload"))
    return results
