"""Document loading helpers for docsctx."""

from __future__ import annotations

from dataclasses import dataclass
from html.parser import HTMLParser
from pathlib import Path
from urllib.parse import urljoin, urlparse

import httpx


@dataclass(slots=True)
class LoadedDocument:
    source_uri: str
    title: str
    doc_type: str
    content: str


class _HTMLTextExtractor(HTMLParser):
    def __init__(self) -> None:
        super().__init__()
        self._title_parts: list[str] = []
        self._text_parts: list[str] = []
        self._links: list[str] = []
        self._in_title = False
        self._skip_depth = 0

    def handle_starttag(self, tag: str, attrs: list[tuple[str, str | None]]) -> None:
        if tag in {"script", "style"}:
            self._skip_depth += 1
            return
        if tag == "title":
            self._in_title = True
        if tag == "a":
            href = dict(attrs).get("href")
            if href:
                self._links.append(href)
        if tag in {"p", "div", "section", "article", "li", "h1", "h2", "h3", "h4", "h5", "h6", "br"}:
            self._text_parts.append("\n")

    def handle_endtag(self, tag: str) -> None:
        if tag in {"script", "style"} and self._skip_depth > 0:
            self._skip_depth -= 1
            return
        if tag == "title":
            self._in_title = False
        if tag in {"p", "div", "section", "article", "li"}:
            self._text_parts.append("\n")

    def handle_data(self, data: str) -> None:
        if self._skip_depth > 0:
            return
        stripped = data.strip()
        if not stripped:
            return
        if self._in_title:
            self._title_parts.append(stripped)
        self._text_parts.append(stripped)

    @property
    def title(self) -> str:
        return " ".join(self._title_parts).strip()

    @property
    def text(self) -> str:
        return _normalize_text(" ".join(self._text_parts))

    @property
    def links(self) -> list[str]:
        return self._links


def _normalize_text(content: str) -> str:
    lines = [line.strip() for line in content.replace("\r", "").split("\n")]
    filtered = [line for line in lines if line]
    return "\n".join(filtered).strip()


def _markdown_to_text(content: str) -> str:
    lines: list[str] = []
    for raw_line in content.splitlines():
        line = raw_line.strip()
        if not line:
            lines.append("")
            continue
        if line.startswith("#"):
            lines.append(line.lstrip("#").strip())
            continue
        if line.startswith(("- ", "* ")):
            lines.append(line[2:].strip())
            continue
        lines.append(line.replace("`", ""))
    return _normalize_text("\n".join(lines))


def _load_pdf(path: Path) -> LoadedDocument | None:
    try:
        import pymupdf  # type: ignore[import]
        title = path.stem
        doc = pymupdf.open(str(path))
        pages: list[str] = []
        for page in doc:
            pages.append(page.get_text())
        doc.close()
        return LoadedDocument(
            source_uri=str(path),
            title=title,
            doc_type="pdf",
            content=_normalize_text("\n".join(pages)),
        )
    except Exception as e:
        import typer
        typer.secho(f"⚠️  Failed to load PDF {path}: {e}", fg=typer.colors.RED)
        return None


def _load_docx(path: Path) -> LoadedDocument | None:
    try:
        import docx as _docx  # type: ignore[import]
        title = path.stem
        document = _docx.Document(str(path))
        paragraphs = [p.text for p in document.paragraphs if p.text.strip()]
        return LoadedDocument(
            source_uri=str(path),
            title=title,
            doc_type="docx",
            content=_normalize_text("\n".join(paragraphs)),
        )
    except Exception as e:
        import typer
        typer.secho(f"⚠️  Failed to load DOCX {path}: {e}", fg=typer.colors.RED)
        return None


def load_path(path: Path) -> LoadedDocument | None:
    suffix = path.suffix.lower()
    title = path.stem

    if suffix == ".pdf":
        return _load_pdf(path)
    if suffix in {".docx", ".doc"}:
        return _load_docx(path)

    content = path.read_text(encoding="utf-8", errors="ignore")

    if suffix in {".txt", ".text"}:
        return LoadedDocument(source_uri=str(path), title=title, doc_type="text", content=_normalize_text(content))
    if suffix in {".md", ".markdown"}:
        first_heading = next((line.lstrip("#").strip() for line in content.splitlines() if line.startswith("#")), "")
        return LoadedDocument(
            source_uri=str(path),
            title=first_heading or title,
            doc_type="markdown",
            content=_markdown_to_text(content),
        )
    if suffix in {".html", ".htm"}:
        parser = _HTMLTextExtractor()
        parser.feed(content)
        return LoadedDocument(
            source_uri=str(path),
            title=parser.title or title,
            doc_type="html",
            content=parser.text,
        )
    return None


def fetch_url(url: str, timeout: float = 10.0) -> tuple[LoadedDocument, list[str]]:
    response = httpx.get(url, timeout=timeout, follow_redirects=True)
    response.raise_for_status()
    parser = _HTMLTextExtractor()
    parser.feed(response.text)
    title = parser.title or urlparse(str(response.url)).path.rsplit("/", 1)[-1] or str(response.url)
    normalized_links = [urljoin(str(response.url), link) for link in parser.links]
    document = LoadedDocument(source_uri=str(response.url), title=title, doc_type="html", content=parser.text)
    return document, normalized_links


def fetch_sitemap(base_url: str, timeout: float = 10.0) -> list[str]:
    """Fetch and parse sitemap.xml, returning a list of page URLs."""
    from xml.etree import ElementTree as ET

    candidates = [
        urljoin(base_url, "/sitemap.xml"),
        urljoin(base_url, "/sitemap_index.xml"),
        urljoin(base_url, "/sitemap/sitemap.xml"),
    ]
    urls: list[str] = []
    for sitemap_url in candidates:
        try:
            response = httpx.get(sitemap_url, timeout=timeout, follow_redirects=True)
            if response.status_code != 200:
                continue
            root = ET.fromstring(response.text)
            ns = {"sm": "http://www.sitemaps.org/schemas/sitemap/0.9"}
            # Sitemap index: contains <sitemap> children with <loc> pointing to sub-sitemaps
            for sitemap in root.findall("sm:sitemap", ns):
                loc = sitemap.findtext("sm:loc", namespaces=ns)
                if loc:
                    try:
                        sub_resp = httpx.get(loc.strip(), timeout=timeout, follow_redirects=True)
                        if sub_resp.status_code == 200:
                            sub_root = ET.fromstring(sub_resp.text)
                            for url_el in sub_root.findall("sm:url", ns):
                                page_loc = url_el.findtext("sm:loc", namespaces=ns)
                                if page_loc:
                                    urls.append(page_loc.strip())
                    except Exception:
                        pass
            # Regular sitemap: contains <url> children
            for url_el in root.findall("sm:url", ns):
                loc = url_el.findtext("sm:loc", namespaces=ns)
                if loc:
                    urls.append(loc.strip())
            if urls:
                break
        except Exception:
            continue
    return urls
