"""Resolve target website from NL prompt, goal memory, or defaults (0.2.2.44)."""

from __future__ import annotations

import re
from dataclasses import dataclass
from typing import Literal
from urllib.parse import urlparse

from iuve_connect_agents.shared.url_normalizer import (
    extract_normalized_url_from_text,
    normalize_avatar_url,
)

SiteSource = Literal["prompt", "memory", "default"]

_FILE_EXT_DOMAIN_BLOCKLIST = frozenset(
    {
        "md",
        "txt",
        "html",
        "htm",
        "json",
        "xml",
        "csv",
        "pdf",
        "doc",
        "docx",
        "py",
        "js",
        "ts",
        "css",
    }
)
_FILENAME_IN_PROMPT_RE = re.compile(
    r"\b([A-Za-z0-9_.-]+\.(?:md|txt|html|htm|json|xml|csv|pdf|doc|docx))\b",
    re.IGNORECASE,
)
_PLATFORM_PATTERNS: tuple[tuple[re.Pattern[str], str, str], ...] = (
    (re.compile(r"\b(?:x\.com|twitter|твиттер)\b", re.IGNORECASE), "x", "https://x.com/"),
    (re.compile(r"\blinked\s*in\b|\bлинкедин\b|\bлинкед\s*ин\b", re.IGNORECASE), "linkedin", "https://www.linkedin.com/"),
    (re.compile(r"\bfacebook\b|\bфейсбук\b", re.IGNORECASE), "facebook", "https://www.facebook.com/"),
    (re.compile(r"\bwordpress\b|\bвордпресс\b", re.IGNORECASE), "wordpress", "https://wordpress.com/"),
    (re.compile(r"\bmedium\b|\bмедиум\b", re.IGNORECASE), "medium", "https://medium.com/"),
    (re.compile(r"\bmastodon\b|\bмастодон\b", re.IGNORECASE), "mastodon", "https://mastodon.social/"),
    (re.compile(r"\binstagram\b|\bинстаграм\b", re.IGNORECASE), "instagram", "https://www.instagram.com/"),
    (re.compile(r"\btiktok\b|\bтик\s*ток\b", re.IGNORECASE), "tiktok", "https://www.tiktok.com/"),
    (re.compile(r"\breddit\b|\bреддит\b", re.IGNORECASE), "reddit", "https://www.reddit.com/"),
    (re.compile(r"\btelegram\b|\bтелеграм\b", re.IGNORECASE), "telegram", "https://web.telegram.org/"),
    (re.compile(r"\bchatgpt\b|\bchat\.openai\b", re.IGNORECASE), "chatgpt", "https://chatgpt.com/"),
)
_POST_CONTEXT_RE = re.compile(
    r"\b(?:пост|post|опублик|publish|черновик|draft|стать|article)\b",
    re.IGNORECASE,
)
_SITE_CONTEXT_RE = re.compile(
    r"\b(?:на\s+сайт|на\s+сайте|сайт|site|url|браузер|browser|"
    r"для|for|в|in|on|at)\b",
    re.IGNORECASE,
)
_PUBLISH_ON_EXPLICIT_SITE_RE = re.compile(
    r"\b(?:"
    r"опублик(?:уй|овать)\s+на|"
    r"пост\s+на\s+сайт(?:е)?|"
    r"publish\s+on|"
    r"post\s+on(?:\s+the)?\s+site"
    r")\b",
    re.IGNORECASE,
)


@dataclass(frozen=True)
class TargetSite:
    url: str
    platform: str
    source: SiteSource

    def as_dict(self) -> dict[str, str]:
        return {"url": self.url, "platform": self.platform, "source": self.source}


def _blocked_filename_domains(text: str) -> frozenset[str]:
    blocked: set[str] = set()
    for match in _FILENAME_IN_PROMPT_RE.finditer(str(text or "")):
        name = str(match.group(1) or "").strip().lower()
        if "." in name:
            blocked.add(name)
            blocked.add(name.rsplit(".", 1)[0])
    return frozenset(blocked)


def _is_blocked_domain(domain: str, *, blocked_names: frozenset[str]) -> bool:
    host = str(domain or "").strip().lower().removeprefix("www.")
    if not host:
        return True
    if host in blocked_names:
        return True
    ext = host.rsplit(".", 1)[-1] if "." in host else ""
    if ext in _FILE_EXT_DOMAIN_BLOCKLIST:
        return True
    return False


def extract_url_from_prompt(text: str) -> str | None:
    """Extract https URL or domain from prompt; skip workspace filenames like report.md."""
    raw = str(text or "").strip()
    if not raw:
        return None
    blocked = _blocked_filename_domains(raw)
    url = extract_normalized_url_from_text(raw, correct_typos=False)
    if url:
        host = (urlparse(url).hostname or "").strip().lower()
        if host and not _is_blocked_domain(host, blocked_names=blocked):
            return url
    domain_match = re.search(
        r"\b(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,63}\b",
        raw,
    )
    if domain_match:
        domain = domain_match.group(0)
        if not _is_blocked_domain(domain, blocked_names=blocked):
            return normalize_avatar_url(domain)
    return None


def extract_platform_from_prompt(text: str) -> TargetSite | None:
    """Map platform name in prompt to canonical URL."""
    raw = str(text or "").strip()
    if not raw:
        return None
    for pattern, platform, url in _PLATFORM_PATTERNS:
        if pattern.search(raw):
            return TargetSite(url=url, platform=platform, source="prompt")
    return None


def prompt_specifies_site(text: str) -> bool:
    """True when current prompt names a URL, domain, or known platform."""
    if extract_url_from_prompt(text):
        return True
    if extract_platform_from_prompt(text):
        return True
    return False


def _url_host(url: str) -> str:
    return (urlparse(url).hostname or "").strip().lower().removeprefix("www.")


def _url_is_explicit_publish_destination(text: str, url: str) -> bool:
    """True when user names the URL's site as the publish target (not research/source)."""
    raw = str(text or "").strip()
    if not raw or not url:
        return False
    host = _url_host(url)
    if not host:
        return False
    host_escaped = re.escape(host)
    if _PUBLISH_ON_EXPLICIT_SITE_RE.search(raw):
        if re.search(
            rf"(?:https?://)?(?:www\.)?{host_escaped}\b",
            raw,
            re.IGNORECASE,
        ):
            return True
    if re.search(
        rf"\bна\s+сайт(?:е)?\s+(?:https?://)?(?:www\.)?{host_escaped}\b",
        raw,
        re.IGNORECASE,
    ):
        return True
    if re.search(
        rf"\bpost\s+on\s+(?:https?://)?(?:www\.)?{host_escaped}\b",
        raw,
        re.IGNORECASE,
    ):
        return True
    return False


def _compound_platform_over_url(platform_hit: TargetSite, url: str, text: str) -> bool:
    """Platform keyword + unrelated URL → URL is source, platform is destination."""
    url_host = _url_host(url)
    platform_host = _url_host(platform_hit.url)
    if not url_host or not platform_host:
        return False
    if url_host == platform_host:
        return False
    if _url_is_explicit_publish_destination(text, url):
        return False
    return True


def extract_target_site_from_prompt(text: str) -> TargetSite | None:
    """Resolve site from current prompt only (priority tier 1)."""
    platform_hit = extract_platform_from_prompt(text)
    url = extract_url_from_prompt(text)

    if platform_hit and url:
        if _compound_platform_over_url(platform_hit, url, text):
            return platform_hit
        url_host = _url_host(url)
        platform_host = _url_host(platform_hit.url)
        if url_host == platform_host:
            return platform_hit

    if url:
        host = _url_host(url)
        platform = host.split(".")[0] if host else "site"
        return TargetSite(url=url, platform=platform, source="prompt")
    if platform_hit:
        return platform_hit
    return None


def extract_target_site(
    text: str,
    *,
    memory_site: str = "",
    default_url: str = "",
) -> TargetSite | None:
    """
    Priority: current prompt → goal memory → default.
    Returns None when no site is resolved at any tier.
    """
    from_prompt = extract_target_site_from_prompt(text)
    if from_prompt:
        return from_prompt
    mem = str(memory_site or "").strip()
    if mem:
        normalized = normalize_avatar_url(mem) or mem
        host = (urlparse(normalized).hostname or "").strip().lower().removeprefix("www.")
        platform = host.split(".")[0] if host else "site"
        return TargetSite(url=normalized, platform=platform, source="memory")
    default = str(default_url or "").strip()
    if default:
        normalized = normalize_avatar_url(default) or default
        host = (urlparse(normalized).hostname or "").strip().lower().removeprefix("www.")
        platform = host.split(".")[0] if host else "site"
        return TargetSite(url=normalized, platform=platform, source="default")
    return None


def is_web_post_with_site(text: str, *, memory_site: str = "") -> bool:
    """True when user wants a web/social post and a target site is known."""
    raw = str(text or "").strip()
    if not raw:
        return False
    try:
        from .compound_intent_resolver import should_defer_web_post_intent

        if should_defer_web_post_intent(raw):
            return False
    except ImportError:
        pass
    lower = raw.lower()
    has_post = bool(_POST_CONTEXT_RE.search(raw))
    has_create = any(
        token in lower
        for token in ("создай", "создать", "create ", "make ", "сделай", "напиш", "написать", "write ")
    )
    if not has_post and not (has_create and _SITE_CONTEXT_RE.search(raw)):
        return False
    if not has_create and not has_post:
        return False
    site = extract_target_site(raw, memory_site=memory_site)
    return site is not None


def extract_target_url(text: str, *, memory_site: str = "") -> str | None:
    """Backward-compatible URL extraction with prompt-first priority."""
    site = extract_target_site(text, memory_site=memory_site)
    return site.url if site else None
