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 (RP web research prototype)"


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


def _domain(url: str) -> str:
    return re.sub(r"^www\.", "", re.sub(r"^https?://", "", url or "").split("/", 1)[0])


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


async def _wikidata_search(client: httpx.AsyncClient, query: str, limit: int = 6) -> list[dict[str, Any]]:
    r = await client.get(
        WIKIDATA_API,
        params={"action": "wbsearchentities", "search": query, "language": "en", "uselang": "en", "limit": limit, "format": "json"},
        headers={"User-Agent": USER_AGENT},
    )
    r.raise_for_status()
    out: list[dict[str, Any]] = []
    for row in r.json().get("search", []):
        qid = str(row.get("id") or "")
        title = str(row.get("label") or qid)
        out.append({
            "id": f"wikidata:{qid}",
            "title": title,
            "summary": _clean(str(row.get("description") or "")),
            "url": str(row.get("concepturi") or f"https://www.wikidata.org/wiki/{qid}"),
            "domain": "wikidata.org",
            "source_label": "Wikidata",
            "source_kind": "structured_reference",
            "authority": "structured reference",
        })
    return out


async def _brave_search(client: httpx.AsyncClient, query: str, key: str, limit: int = 10) -> list[dict[str, Any]]:
    r = await client.get(
        BRAVE_API,
        params={"q": query, "count": limit, "safesearch": "moderate"},
        headers={"Accept": "application/json", "X-Subscription-Token": key, "User-Agent": USER_AGENT},
    )
    r.raise_for_status()
    out: list[dict[str, Any]] = []
    for i, row in enumerate(r.json().get("web", {}).get("results", []), 1):
        url = str(row.get("url") or "")
        out.append({
            "id": f"web:{i}:{url}",
            "title": str(row.get("title") or "Web result"),
            "summary": _clean(str(row.get("description") or "")),
            "url": url,
            "domain": _domain(url),
            "source_label": _domain(url) or "Web result",
            "source_kind": "web",
            "authority": "needs review",
        })
    return out


async def search_web(query: str) -> dict[str, Any]:
    query = (query or "").strip()
    if not query:
        raise ValueError("Search text is required")
    if len(query) > 300:
        raise ValueError("Search text is too long")

    timeout = float(os.environ.get("WEB_SEARCH_TIMEOUT", os.environ.get("CANON_SEARCH_TIMEOUT", "12")) or 12)
    brave_key = (os.environ.get("BRAVE_SEARCH_API_KEY") or "").strip()
    warnings: list[str] = []
    results: list[dict[str, Any]] = []

    async with httpx.AsyncClient(timeout=timeout, follow_redirects=True) as client:
        try:
            results.extend(await _wiki_search(client, query))
        except Exception as exc:
            warnings.append(f"Wikipedia lookup failed: {type(exc).__name__}")
        try:
            results.extend(await _wikidata_search(client, query))
        except Exception as exc:
            warnings.append(f"Wikidata lookup failed: {type(exc).__name__}")
        if brave_key:
            try:
                # Put broad-web results first when available; Wikipedia/Wikidata remain free fallbacks.
                results = await _brave_search(client, query, brave_key) + results
            except Exception as exc:
                warnings.append(f"Broad web search failed: {type(exc).__name__}")

    seen: set[str] = set()
    deduped: list[dict[str, Any]] = []
    for row in results:
        url = str(row.get("url") or "")
        key = url or f"{row.get('title')}::{row.get('summary')}"
        if key in seen:
            continue
        seen.add(key)
        deduped.append(row)

    mode = "Wikipedia + Wikidata"
    if brave_key:
        mode += " + broad web search"
    return {
        "query": query,
        "mode": mode,
        "broad_web_enabled": bool(brave_key),
        "results": deduped[:20],
        "warnings": warnings,
        "rule": "Search results are player-only until the user explicitly chooses Use in RP or Save to World.",
    }
