"""WEB_RESEARCH intent — browser search + extract + summarize pipeline (0.2.2.51)."""

from __future__ import annotations

import json
import re
from dataclasses import dataclass
from typing import Any, Literal
from urllib.parse import quote_plus

_WEB_INTERNET_RE = re.compile(
    r"\b(?:в\s+интернет\w*|in\s+the\s+internet|online|в\s+сети|google|гугл|интернет\w*)\b",
    re.IGNORECASE,
)
_WEB_SEARCH_RE = re.compile(
    r"\b(?:поиск\w*|search|найди|найти|ищи|find|собери)\b",
    re.IGNORECASE,
)
_LOCAL_COMPUTER_RE = re.compile(
    r"\b(?:поиск\w*\s+на\s+компьютер\w*|search\s+on\s+(?:the\s+)?computer|на\s+компьютер\w*)\b",
    re.IGNORECASE,
)
_JOB_AD_RE = re.compile(
    r"\b(?:объявлен\w*|ваканс\w*|job\s+ad|jobs?|работ\w*|driver|водител\w*)\b",
    re.IGNORECASE,
)
_TOPIC_LOCATION_RE = re.compile(
    r"\b(?:в\s+город\w*|in\s+city|город\w*|city\s+of)\s+([A-Za-zА-Яа-яЁёÄäÖöÜüß\-\s]+)",
    re.IGNORECASE,
)
_ABOUT_TOPIC_RE = re.compile(
    r"(?:информаци\w*\s+)?(?:о|об|про|about)\s+([^,.;!?]+)",
    re.IGNORECASE,
)
_INTERNET_SOURCE_RE = re.compile(
    r"\b(?:"
    r"из\s+интернет\w*|со\s+всего\s+интернет\w*|"
    r"information\s+from\s+(?:the\s+)?internet|"
    r"from\s+(?:the\s+)?internet|"
    r"online\s+research|"
    r"с\s+интернет\w*"
    r")\b",
    re.IGNORECASE,
)
_DOSSIER_RE = re.compile(r"\b(?:досье|dossier)\b", re.IGNORECASE)
_SEARCH_PREFIX_RE = re.compile(
    r"^(?:"
    r"(?:выполни|сделай|проведи)\s+(?:поиск\w*|search)\s+(?:в\s+интернет\w*|online)?\s*|"
    r"(?:найди|найти|find|search|ищи)\s+(?:в\s+интернет\w*\s+)?(?:информаци\w*\s+(?:в\s+интернет\w*\s+)?)?"
    r")",
    re.IGNORECASE,
)

_EXTRACTION_FAIL_NOTE = (
    "Открыт поиск в браузере; автоматический сбор результатов ограничен — "
    "просмотрите результаты в браузере."
)
_MVP_NOTE = (
    "Сбор объявлений — MVP: vision/текстовая выжимка топ-N результатов Google. "
    "Прямой scraping job-сайтов не выполняется."
)
_TOS_LIMIT_NOTE = (
    "Indeed, LinkedIn и аналоги: только ручной просмотр или официальные API — "
    "автоматический парсинг карточек вакансий вне scope MVP."
)
_HONEST_NO_RESULTS = "Не удалось извлечь результаты поиска автоматически."


@dataclass(frozen=True)
class WebResearchIntent:
    intent: Literal["WEB_RESEARCH"] = "WEB_RESEARCH"
    query: str = ""
    search_url: str = ""
    mvp_note: str = ""
    collect_job_ads: bool = False

    def as_dict(self) -> dict[str, Any]:
        return {
            "intent": self.intent,
            "query": self.query,
            "search_url": self.search_url,
            "mvp_note": self.mvp_note,
            "collect_job_ads": self.collect_job_ads,
        }


def _extract_search_query(text: str) -> str:
    raw = str(text or "").strip()
    lower = raw.lower()
    parts: list[str] = []

    loc_match = _TOPIC_LOCATION_RE.search(raw)
    if loc_match:
        parts.append(str(loc_match.group(1) or "").strip(" .,…"))

    if "мюнхен" in lower or "münchen" in lower or "munich" in lower:
        if not any("мюнхен" in p.lower() or "münchen" in p.lower() for p in parts):
            parts.append("Мюнхен")
    if re.search(r"\bводител\w*\b", lower) or "driver" in lower:
        parts.append("водитель")
    if re.search(r"\bкатегор\w*\s+b\b", lower) or "category b" in lower:
        parts.append("категория B")
    if _JOB_AD_RE.search(raw):
        parts.append("вакансии")

    about_match = _ABOUT_TOPIC_RE.search(raw)
    if about_match:
        topic = str(about_match.group(1) or "").strip(" .,…")
        if topic and len(topic) > 2:
            parts.append(topic)

    if not parts:
        cleaned = _SEARCH_PREFIX_RE.sub("", raw).strip(" .,…")
        if cleaned and len(cleaned) > 4:
            parts.append(cleaned[:120])

    query = " ".join(dict.fromkeys(p for p in parts if p)).strip()
    return query or "вакансии водитель"


def extract_research_topic(text: str, *, default: str = "") -> str:
    """Topic/subject for dossier or document from internet research prompts."""
    raw = str(text or "").strip()
    if not raw:
        return default
    about_match = _ABOUT_TOPIC_RE.search(raw)
    if about_match:
        topic = str(about_match.group(1) or "").strip(" .,…")
        if topic and len(topic) > 2:
            return topic
    query = _extract_search_query(raw)
    if query and query not in {"вакансии водитель", "вакансии"}:
        return query
    return default


def dossier_filename_from_topic(topic: str, *, language: str = "") -> str:
    """Build workspace filename like Iurie_Verejan_dossier.md from topic."""
    slug = re.sub(r"[^\w\s\-]", "", str(topic or "").strip())
    slug = re.sub(r"\s+", "_", slug).strip("_")
    if not slug:
        return "dossier.md"
    base = slug if slug.lower().endswith("_dossier") else f"{slug}_dossier"
    return f"{base}.md"


def is_web_research_intent(text: str) -> bool:
    """Internet search — browser + Google query, not consultation stub."""
    raw = str(text or "").strip()
    if not raw:
        return False
    if is_research_then_artifact_intent(raw):
        return False
    if _LOCAL_COMPUTER_RE.search(raw):
        return False
    if not _WEB_SEARCH_RE.search(raw):
        return False
    if _WEB_INTERNET_RE.search(raw):
        return True
    if _JOB_AD_RE.search(raw) and not re.search(
        r"\b(?:папк|folder|файл|file|загрузк|downloads)\b", raw, re.I
    ):
        return True
    return False


def is_research_then_artifact_intent(text: str) -> bool:
    """Document/dossier that must be grounded in web research (not LLM-only)."""
    raw = str(text or "").strip()
    if not raw:
        return False
    lower = raw.lower()
    from .content_creation_intent import is_document_generation_intent

    has_document = bool(is_document_generation_intent(raw)) or bool(_DOSSIER_RE.search(raw))
    has_internet = bool(
        _WEB_INTERNET_RE.search(raw)
        or _INTERNET_SOURCE_RE.search(raw)
        or (_WEB_SEARCH_RE.search(raw) and _WEB_INTERNET_RE.search(raw))
    )
    has_create = any(
        token in lower
        for token in ("создай", "create", "сформир", "напиши", "write", "сделай")
    )
    return has_document and has_internet and has_create


def is_local_computer_job_search_intent(text: str) -> bool:
    """«поиск на компьютере» + job ads — LOCAL_SEARCH keyword scan."""
    raw = str(text or "").strip()
    if not raw:
        return False
    if _LOCAL_COMPUTER_RE.search(raw):
        return True
    if _JOB_AD_RE.search(raw) and re.search(
        r"\b(?:на\s+компьютер\w*|on\s+(?:this\s+)?computer|локальн\w*)\b", raw, re.I
    ):
        return True
    return False


def parse_web_research_intent(text: str) -> WebResearchIntent | None:
    if not is_web_research_intent(text):
        return None
    query = _extract_search_query(text)
    url = f"https://www.google.com/search?q={quote_plus(query)}"
    collect = bool(_JOB_AD_RE.search(text))
    note = _MVP_NOTE if collect else _EXTRACTION_FAIL_NOTE
    return WebResearchIntent(
        query=query,
        search_url=url,
        mvp_note=note,
        collect_job_ads=collect,
    )


def _host_from_url(url: str) -> str:
    try:
        from urllib.parse import urlparse

        host = urlparse(str(url or "")).hostname or ""
        return host.replace("www.", "")
    except Exception:
        return ""


def extract_search_results_from_text(
    page_text: str,
    *,
    max_results: int = 8,
) -> list[dict[str, str]]:
    """Best-effort parse of Google SERP plain text (bounded MVP; no heavy scraping)."""
    text = re.sub(r"\s+", " ", str(page_text or "")).strip()
    if not text:
        return []
    results: list[dict[str, str]] = []
    for match in re.finditer(r"(https?://[^\s<>\"']+)", text):
        url = match.group(1).rstrip(".,)")
        if "google." in url and "/search" in url:
            continue
        title_start = max(0, match.start() - 120)
        title_chunk = text[title_start : match.start()].strip()
        title = title_chunk.split(" — ")[-1].split(" | ")[-1].strip()[-100:]
        if not title or len(title) < 4:
            title = url
        host = _host_from_url(url)
        results.append(
            {
                "title": title,
                "url": url,
                "source": host,
                "snippet": title,
                "confidence": "0.72" if host else "0.5",
            }
        )
        if len(results) >= max_results:
            break
    return results


_MIN_JOB_TABLE_ROWS = 3


def format_job_results_markdown(
    results: list[dict[str, str]],
    *,
    query: str = "",
    extraction_ok: bool = True,
    min_rows: int = _MIN_JOB_TABLE_ROWS,
) -> str:
    if not results:
        return _EXTRACTION_FAIL_NOTE
    lines = [f"## Результаты поиска: {query or 'вакансии'}", ""]
    lines.append("| title | source | url | snippet | confidence |")
    lines.append("| --- | --- | --- | --- | --- |")
    for item in results:
        title = str(item.get("title") or item.get("name") or "Результат").strip()
        url = str(item.get("url") or "").strip()
        source = str(item.get("source") or _host_from_url(url) or "web").strip()
        snippet = str(item.get("snippet") or title).strip()[:120]
        confidence = str(item.get("confidence") or ("0.75" if url else "0.45"))
        safe_title = title.replace("|", "/")
        safe_snippet = snippet.replace("|", "/")
        lines.append(f"| {safe_title} | {source} | {url or '—'} | {safe_snippet} | {confidence} |")
    if len(results) < min_rows:
        lines.extend(
            [
                "",
                (
                    f"_Найдено {len(results)} из {min_rows}+ ожидаемых строк таблицы. "
                    f"{_EXTRACTION_FAIL_NOTE}_"
                ),
            ]
        )
    if not extraction_ok:
        lines.extend(["", f"_{_EXTRACTION_FAIL_NOTE}_"])
    return "\n".join(lines)


def format_web_research_summary_markdown(
    results: list[dict[str, str]],
    *,
    query: str = "",
    extraction_ok: bool = True,
) -> str:
    """General web research summary (non-job queries)."""
    if not results:
        return f"## Поиск: {query or 'интернет'}\n\n_{_HONEST_NO_RESULTS}_"
    lines = [f"## Результаты поиска: {query or 'интернет'}", ""]
    for index, item in enumerate(results, start=1):
        title = str(item.get("title") or "Результат").strip()
        url = str(item.get("url") or "").strip()
        source = str(item.get("source") or _host_from_url(url) or "web").strip()
        snippet = str(item.get("snippet") or title).strip()[:200]
        lines.append(f"{index}. **{title}** ({source})")
        if url:
            lines.append(f"   - {url}")
        if snippet and snippet != title:
            lines.append(f"   - {snippet}")
        lines.append("")
    if not extraction_ok:
        lines.append(f"_{_HONEST_NO_RESULTS}_")
    return "\n".join(lines).strip()


def fetch_google_results_text(search_url: str, *, max_results: int = 8) -> tuple[list[dict[str, str]], str]:
    """Try bounded local fetch of Google SERP; honest note when blocked."""
    from .web_fetch_local import fetch_url_bounded_local

    fetched = fetch_url_bounded_local(search_url)
    if not fetched.get("ok"):
        return [], str(fetched.get("error") or "fetch_failed")
    results = extract_search_results_from_text(str(fetched.get("text") or ""), max_results=max_results)
    return results, ""


def _web_research_pipeline_steps(
    intent: WebResearchIntent,
    *,
    id_prefix: str = "s",
    min_results: int | None = None,
) -> list[dict[str, Any]]:
    """Full pipeline: open search → wait → capture → extract → summarize to chat."""
    if min_results is None:
        min_results = 3 if intent.collect_job_ads else 1
    open_id = f"{id_prefix}1_open_search"
    wait_id = f"{id_prefix}2_wait_results"
    extract_id = f"{id_prefix}3_extract_results"
    summarize_id = f"{id_prefix}4_summarize"
    return [
        {
            "step_id": open_id,
            "command_type": "browser_open_url",
            "payload": {
                "url": intent.search_url,
                "new_window": True,
                "browser": "default",
                "search_query": intent.query,
                "web_research_intent": intent.as_dict(),
                "mvp_limitation": intent.mvp_note,
                "user_consent_state": "consented_via_connect_flow",
            },
        },
        {
            "step_id": wait_id,
            "command_type": "capture_screenshot",
            "depends_on": [open_id],
            "payload": {
                "region": "full",
                "delay_ms": 2500,
                "screen_vision_granted": True,
                "web_research_capture": True,
                "search_query": intent.query,
            },
        },
        {
            "step_id": extract_id,
            "command_type": "verify_screen_contains_text",
            "depends_on": [wait_id],
            "payload": {
                "expected_text": intent.query.split()[0] if intent.query else "результат",
                "depends_on_step": wait_id,
                "match_mode": "contains",
                "verify_mode": "web_research_extract",
                "search_url": intent.search_url,
                "search_query": intent.query,
                "max_results": 8,
                "min_results": min_results,
                "collect_job_ads": intent.collect_job_ads,
                "screen_vision_granted": True,
                "extraction_fail_note": _EXTRACTION_FAIL_NOTE,
                "tos_limit_note": _TOS_LIMIT_NOTE,
            },
        },
        {
            "step_id": summarize_id,
            "command_type": "summarize_content",
            "depends_on": [extract_id],
            "payload": {
                "content_from_step": extract_id,
                "goal_summary": f"Web research summary: {intent.query}",
                "present_to_chat": True,
                "verify_mode": "research_completed",
                "search_query": intent.query,
                "user_consent_state": "consented_via_connect_flow",
            },
        },
    ]


def build_web_research_plan(text: str) -> dict[str, Any] | None:
    intent = parse_web_research_intent(text)
    if intent is None:
        return None

    steps = _web_research_pipeline_steps(intent, id_prefix="s")

    return {
        "goal_summary": f"Поиск в интернете: {intent.query}",
        "verifier": "research_completed",
        "mvp_limitation": intent.mvp_note,
        "tos_limit_note": _TOS_LIMIT_NOTE,
        "web_research": intent.as_dict(),
        "steps": steps,
    }


def build_research_then_artifact_plan(text: str) -> dict[str, Any] | None:
    """Web research → generate_content(research) → write_file → verify."""
    if not is_research_then_artifact_intent(text):
        return None

    from .content_creation_intent import extract_content_topic

    topic = extract_research_topic(text) or extract_content_topic(text, default="research subject")
    query = _extract_search_query(text) or topic
    search_url = f"https://www.google.com/search?q={quote_plus(query)}"
    collect_job_ads = bool(_JOB_AD_RE.search(text))
    intent = WebResearchIntent(
        query=query,
        search_url=search_url,
        mvp_note=_MVP_NOTE if collect_job_ads else _EXTRACTION_FAIL_NOTE,
        collect_job_ads=collect_job_ads,
    )

    lower = str(text or "").lower()
    english = bool(re.search(r"\b(?:на\s+английск\w*|in\s+english|english\s+language)\b", lower))
    filename = dossier_filename_from_topic(topic)
    rel_path = f"IuVeWorkspace/{filename}"

    research_steps = _web_research_pipeline_steps(intent, id_prefix="s_wr")
    extract_step = "s_wr3_extract_results"
    generate_id = "s_gen"
    write_id = "s_write"
    verify_id = "s_verify"

    steps: list[dict[str, Any]] = list(research_steps)
    steps.extend(
        [
            {
                "step_id": generate_id,
                "command_type": "generate_content",
                "depends_on": [extract_step],
                "payload": {
                    "content_type": "document",
                    "topic": topic,
                    "language": "english" if english else "",
                    "research_context_from_step": extract_step,
                    "content_from_step": extract_step,
                    "goal_summary": f"Generate dossier about {topic} from web research",
                    "user_consent_state": "consented_via_connect_flow",
                },
            },
            {
                "step_id": write_id,
                "command_type": "write_file_in_workspace",
                "depends_on": [generate_id],
                "payload": {
                    "relative_path": rel_path,
                    "content_from_step": generate_id,
                    "artifact_topic": topic,
                    "user_consent_state": "consented_via_connect_flow",
                },
            },
            {
                "step_id": verify_id,
                "command_type": "verify_workspace_file",
                "depends_on": [write_id],
                "payload": {
                    "relative_path": rel_path,
                    "topic": topic,
                    "verify_mode": "exists",
                    "user_consent_state": "consented_via_connect_flow",
                },
            },
        ]
    )

    return {
        "goal_summary": f"Досье {filename} — {topic} (из интернета)",
        "verifier": "document_generated",
        "compound_intent": "RESEARCH_THEN_ARTIFACT",
        "last_artifact_topic": topic,
        "last_artifact_filename": filename,
        "web_research": intent.as_dict(),
        "steps": steps,
    }


def build_local_computer_job_search_plan(text: str) -> dict[str, Any] | None:
    """Keyword scan in Downloads/Documents/Desktop when no explicit folder given."""
    from .local_search_resolver import build_local_computer_search_plan

    return build_local_computer_search_plan(text)


def summarize_web_research_evidence(
    *,
    search_query: str,
    search_url: str = "",
    screenshot_text: str = "",
    fetched_results: list[dict[str, str]] | None = None,
    max_results: int = 8,
    collect_job_ads: bool = False,
) -> dict[str, Any]:
    """Build markdown summary from vision text or fetched SERP (executor helper)."""
    results = list(fetched_results or [])
    extraction_ok = bool(results)
    if not results and screenshot_text:
        results = extract_search_results_from_text(screenshot_text, max_results=max_results)
        extraction_ok = bool(results)
    if not results and search_url:
        results, _err = fetch_google_results_text(search_url, max_results=max_results)
        extraction_ok = bool(results)

    if collect_job_ads:
        markdown = format_job_results_markdown(
            results,
            query=search_query,
            extraction_ok=extraction_ok,
            min_rows=3,
        )
    else:
        markdown = format_web_research_summary_markdown(
            results,
            query=search_query,
            extraction_ok=extraction_ok,
        )

    return {
        "ok": extraction_ok,
        "results": results,
        "markdown": markdown,
        "text": markdown,
        "summary": markdown,
        "honest_note": _HONEST_NO_RESULTS if not extraction_ok else "",
        "tos_limit_note": _TOS_LIMIT_NOTE if collect_job_ads else "",
    }
