from __future__ import annotations

import html
import os
import re
from typing import Any
from urllib.parse import quote

import httpx

WIKI_API = "https://en.wikipedia.org/w/api.php"
WIKIDATA_API = "https://www.wikidata.org/w/api.php"
BRAVE_API = "https://api.search.brave.com/res/v1/web/search"
USER_AGENT = "MelumiyaChat/0.4.2 (canon research prototype)"


def _clean_snippet(value: str) -> str:
    value = html.unescape(value or "")
    value = re.sub(r"<[^>]+>", "", value)
    value = re.sub(r"\s+", " ", value).strip()
    return value[:700]


def _confidence(title: str, name: str, franchise: str = "") -> float:
    t = (title or "").lower()
    n = (name or "").strip().lower()
    f = (franchise or "").strip().lower()
    score = 0.55
    if n and n in t:
        score += 0.22
    if f and f in t:
        score += 0.08
    return round(min(score, 0.92), 2)


async def _wiki_search(client: httpx.AsyncClient, query: str, name: str, franchise: str) -> list[dict[str, Any]]:
    params = {
        "action": "query",
        "list": "search",
        "srsearch": query,
        "srlimit": 6,
        "format": "json",
        "utf8": 1,
    }
    r = await client.get(WIKI_API, params=params, headers={"User-Agent": USER_AGENT})
    r.raise_for_status()
    data = r.json()
    out: list[dict[str, Any]] = []
    for row in data.get("query", {}).get("search", []):
        title = str(row.get("title") or "")
        out.append({
            "id": f"wikipedia:{row.get('pageid')}",
            "title": title,
            "url": f"https://en.wikipedia.org/wiki/{quote(title.replace(' ', '_'))}",
            "domain": "en.wikipedia.org",
            "source_kind": "reference",
            "source_label": "Wikipedia",
            "authority": "reputable reference",
            "snippet": _clean_snippet(str(row.get("snippet") or "")),
            "confidence": _confidence(title, name, franchise),
        })
    return out


async def _wikidata_search(client: httpx.AsyncClient, query: str, name: str, franchise: str) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]:
    params = {
        "action": "wbsearchentities",
        "search": query,
        "language": "en",
        "uselang": "en",
        "limit": 6,
        "format": "json",
    }
    r = await client.get(WIKIDATA_API, params=params, headers={"User-Agent": USER_AGENT})
    r.raise_for_status()
    data = r.json()
    sources: list[dict[str, Any]] = []
    facts: list[dict[str, Any]] = []
    for row in data.get("search", []):
        qid = str(row.get("id") or "")
        label = str(row.get("label") or "")
        description = str(row.get("description") or "")
        match = str(row.get("match", {}).get("text") or label)
        sources.append({
            "id": f"wikidata:{qid}",
            "title": label or qid,
            "url": str(row.get("concepturi") or f"https://www.wikidata.org/wiki/{qid}"),
            "domain": "wikidata.org",
            "source_kind": "structured_reference",
            "source_label": "Wikidata",
            "authority": "structured reference",
            "snippet": _clean_snippet(description),
            "confidence": _confidence(match, name, franchise),
        })
        if label:
            facts.append({
                "field": "identity_candidate",
                "value": label,
                "description": description,
                "source_id": f"wikidata:{qid}",
                "confidence": _confidence(match, name, franchise),
                "status": "candidate",
            })
    return sources, facts


def _classify_web_result(url: str) -> tuple[str, str]:
    lower = (url or "").lower()
    if "wikipedia.org" in lower or "wikidata.org" in lower:
        return "reference", "reputable reference"
    if "fandom.com" in lower or "wiki.gg" in lower:
        return "community_reference", "community/reference"
    return "web", "needs review"


async def _brave_search(client: httpx.AsyncClient, query: str, name: str, franchise: str, key: str) -> list[dict[str, Any]]:
    headers = {"Accept": "application/json", "X-Subscription-Token": key, "User-Agent": USER_AGENT}
    r = await client.get(BRAVE_API, params={"q": query, "count": 10, "safesearch": "moderate"}, headers=headers)
    r.raise_for_status()
    data = r.json()
    out: list[dict[str, Any]] = []
    for i, row in enumerate(data.get("web", {}).get("results", []), 1):
        url = str(row.get("url") or "")
        title = str(row.get("title") or "")
        kind, authority = _classify_web_result(url)
        domain = re.sub(r"^www\.", "", re.sub(r"^https?://", "", url).split("/", 1)[0])
        out.append({
            "id": f"web:{i}:{url}",
            "title": title,
            "url": url,
            "domain": domain,
            "source_kind": kind,
            "source_label": domain or "Web result",
            "authority": authority,
            "snippet": _clean_snippet(str(row.get("description") or "")),
            "confidence": _confidence(title, name, franchise),
        })
    return out


async def search_canon(name: str, franchise: str = "") -> dict[str, Any]:
    name = (name or "").strip()
    franchise = (franchise or "").strip()
    if not name:
        raise ValueError("Character name is required")
    if len(name) > 160 or len(franchise) > 160:
        raise ValueError("Search text is too long")

    query = f'"{name}" {franchise}'.strip()
    timeout = float(os.environ.get("CANON_SEARCH_TIMEOUT", "12") or 12)
    brave_key = (os.environ.get("BRAVE_SEARCH_API_KEY") or "").strip()
    sources: list[dict[str, Any]] = []
    facts: list[dict[str, Any]] = []
    warnings: list[str] = []

    async with httpx.AsyncClient(timeout=timeout, follow_redirects=True) as client:
        try:
            wiki = await _wiki_search(client, query, name, franchise)
            sources.extend(wiki)
        except Exception as exc:
            warnings.append(f"Wikipedia lookup failed: {type(exc).__name__}")
        try:
            wd_sources, wd_facts = await _wikidata_search(client, query, name, franchise)
            sources.extend(wd_sources)
            facts.extend(wd_facts)
        except Exception as exc:
            warnings.append(f"Wikidata lookup failed: {type(exc).__name__}")
        if brave_key:
            try:
                sources.extend(await _brave_search(client, query, name, franchise, brave_key))
            except Exception as exc:
                warnings.append(f"Broad web search failed: {type(exc).__name__}")

    # De-duplicate by URL while preserving strongest/earliest result.
    seen: set[str] = set()
    deduped: list[dict[str, Any]] = []
    for s in sorted(sources, key=lambda x: float(x.get("confidence") or 0), reverse=True):
        url = str(s.get("url") or "")
        if not url or url in seen:
            continue
        seen.add(url)
        deduped.append(s)

    mode = "Wikipedia + Wikidata"
    if brave_key:
        mode += " + broad web search"
    return {
        "query": query,
        "character_name": name,
        "franchise": franchise,
        "mode": mode,
        "broad_web_enabled": bool(brave_key),
        "sources": deduped[:18],
        "fact_candidates": facts[:12],
        "warnings": warnings,
        "import_rule": "Nothing becomes canon automatically. Selected sources are attached as research/provenance and remain separate from active AU canon.",
    }
