"""DocumentLoader - 多格式文档解析器

支持 PDF（PyMuPDF/pdfplumber）、Word（python-docx）、Markdown（mistune）、
HTML（BeautifulSoup）、纯文本。所有格式依赖均为可选（try/except ImportError）。
"""

from __future__ import annotations

import logging
import uuid
from dataclasses import dataclass, field
from datetime import datetime, timezone
from pathlib import Path
from typing import Any

logger = logging.getLogger(__name__)


@dataclass
class Document:
    """解析后的文档统一格式"""

    doc_id: str
    title: str
    content: str
    metadata: dict[str, Any] = field(default_factory=dict)

    def __post_init__(self) -> None:
        if "source" not in self.metadata:
            self.metadata["source"] = ""
        if "format" not in self.metadata:
            self.metadata["format"] = "unknown"
        if "page_count" not in self.metadata:
            self.metadata["page_count"] = 0
        if "created_at" not in self.metadata:
            self.metadata["created_at"] = datetime.now(timezone.utc).isoformat()

    def to_dict(self) -> dict[str, Any]:
        return {
            "doc_id": self.doc_id,
            "title": self.title,
            "content": self.content,
            "metadata": self.metadata,
        }


def _detect_format(filename: str) -> str:
    """根据文件扩展名检测文档格式"""
    ext = Path(filename).suffix.lower()
    format_map = {
        ".pdf": "pdf",
        ".docx": "docx",
        ".doc": "docx",
        ".md": "markdown",
        ".markdown": "markdown",
        ".html": "html",
        ".htm": "html",
        ".txt": "text",
        ".csv": "text",
        ".json": "text",
        ".xml": "text",
    }
    return format_map.get(ext, "text")


class DocumentLoader:
    """多格式文档解析器

    支持格式：
    - PDF: PyMuPDF (fitz) → pdfplumber → 纯文本回退
    - Word: python-docx → 纯文本回退
    - Markdown: mistune → 纯文本回退
    - HTML: BeautifulSoup → 纯文本回退
    - 纯文本: 直接读取
    """

    def load(self, file_path: str | Path) -> Document:
        """从文件路径加载文档

        Args:
            file_path: 文件路径

        Returns:
            解析后的 Document 对象

        Raises:
            FileNotFoundError: 文件不存在
            ValueError: 不支持的格式
        """
        path = Path(file_path)
        if not path.exists():
            raise FileNotFoundError(f"File not found: {path}")

        content = path.read_bytes()
        return self.load_bytes(content, path.name)

    def load_bytes(self, content: bytes, filename: str) -> Document:
        """从字节内容加载文档

        Args:
            content: 文件字节内容
            filename: 文件名（用于格式检测和元数据）

        Returns:
            解析后的 Document 对象
        """
        doc_format = _detect_format(filename)
        doc_id = str(uuid.uuid4())

        parsers = {
            "pdf": self._parse_pdf,
            "docx": self._parse_docx,
            "markdown": self._parse_markdown,
            "html": self._parse_html,
            "text": self._parse_text,
        }

        parser = parsers.get(doc_format)
        if parser is None:
            logger.warning(f"Unsupported format '{doc_format}' for {filename}, falling back to text")
            parser = self._parse_text

        text, extra_meta = parser(content, filename)

        metadata: dict[str, Any] = {
            "source": filename,
            "format": doc_format,
            "created_at": datetime.now(timezone.utc).isoformat(),
        }
        metadata.update(extra_meta)

        title = Path(filename).stem
        if "title" in extra_meta:
            title = extra_meta["title"]

        return Document(
            doc_id=doc_id,
            title=title,
            content=text,
            metadata=metadata,
        )

    def _parse_pdf(self, content: bytes, filename: str) -> tuple[str, dict[str, Any]]:
        """解析 PDF 文件

        优先使用 PyMuPDF (fitz)，回退到 pdfplumber，最终回退到纯文本。
        """
        # 尝试 PyMuPDF
        try:
            import fitz  # PyMuPDF

            doc = fitz.open(stream=content, filetype="pdf")
            pages = []
            for page in doc:
                pages.append(page.get_text())
            text = "\n\n".join(pages)
            meta = {
                "page_count": len(doc),
                "parser": "pymupdf",
            }
            # 提取 PDF 元数据中的标题
            pdf_meta = doc.metadata
            if pdf_meta and pdf_meta.get("title"):
                meta["title"] = pdf_meta["title"]
            doc.close()
            return text, meta
        except ImportError:
            pass
        except Exception as e:
            logger.warning(f"PyMuPDF parsing failed for {filename}: {e}")

        # 尝试 pdfplumber
        try:
            import pdfplumber
            import io

            pdf = pdfplumber.open(io.BytesIO(content))
            pages = []
            for page in pdf.pages:
                page_text = page.extract_text()
                if page_text:
                    pages.append(page_text)
            text = "\n\n".join(pages)
            meta = {
                "page_count": len(pdf.pages),
                "parser": "pdfplumber",
            }
            pdf.close()
            return text, meta
        except ImportError:
            pass
        except Exception as e:
            logger.warning(f"pdfplumber parsing failed for {filename}: {e}")

        # 回退到纯文本
        logger.warning(f"No PDF parser available for {filename}, falling back to text extraction")
        return self._parse_text(content, filename)

    def _parse_docx(self, content: bytes, filename: str) -> tuple[str, dict[str, Any]]:
        """解析 Word 文件

        使用 python-docx，回退到纯文本。
        """
        try:
            from docx import Document as DocxDocument
            import io

            doc = DocxDocument(io.BytesIO(content))
            paragraphs = []
            table_count = 0

            # 提取段落文本
            for para in doc.paragraphs:
                if para.text.strip():
                    paragraphs.append(para.text.strip())

            # 提取表格文本
            for table in doc.tables:
                table_count += 1
                for row in table.rows:
                    row_text = " | ".join(cell.text.strip() for cell in row.cells)
                    if row_text.strip(" |"):
                        paragraphs.append(row_text)

            text = "\n\n".join(paragraphs)
            meta = {
                "parser": "python-docx",
                "table_count": table_count,
            }

            # 提取文档属性中的标题
            if doc.core_properties and doc.core_properties.title:
                meta["title"] = doc.core_properties.title

            return text, meta
        except ImportError:
            logger.warning(f"python-docx not available for {filename}, falling back to text")
            return self._parse_text(content, filename)
        except Exception as e:
            logger.warning(f"python-docx parsing failed for {filename}: {e}")
            return self._parse_text(content, filename)

    def _parse_markdown(self, content: bytes, filename: str) -> tuple[str, dict[str, Any]]:
        """解析 Markdown 文件

        使用 mistune（如果可用），否则直接读取文本。
        Markdown 原文保留，因为后续分块需要标题结构。
        """
        try:
            text = content.decode("utf-8")
        except UnicodeDecodeError:
            text = content.decode("utf-8", errors="replace")

        # 提取第一个标题作为文档标题
        title = ""
        for line in text.split("\n"):
            line_stripped = line.strip()
            if line_stripped.startswith("#"):
                title = line_stripped.lstrip("#").strip()
                break

        meta: dict[str, Any] = {
            "parser": "markdown",
        }
        if title:
            meta["title"] = title

        # 尝试用 mistune 提取结构信息（但保留原文用于分块）
        try:
            import mistune

            # 统计标题数量
            heading_count = 0
            for line in text.split("\n"):
                if line.strip().startswith("#"):
                    heading_count += 1
            meta["heading_count"] = heading_count
        except ImportError:
            pass

        return text, meta

    def _parse_html(self, content: bytes, filename: str) -> tuple[str, dict[str, Any]]:
        """解析 HTML 文件

        使用 BeautifulSoup 提取文本，回退到纯文本。
        """
        try:
            from bs4 import BeautifulSoup

            try:
                html_text = content.decode("utf-8")
            except UnicodeDecodeError:
                html_text = content.decode("utf-8", errors="replace")

            soup = BeautifulSoup(html_text, "html.parser")

            # 移除 script 和 style 标签
            for tag in soup(["script", "style"]):
                tag.decompose()

            text = soup.get_text(separator="\n", strip=True)

            # 提取标题
            title = ""
            if soup.title and soup.title.string:
                title = soup.title.string.strip()

            meta: dict[str, Any] = {
                "parser": "beautifulsoup",
            }
            if title:
                meta["title"] = title

            return text, meta
        except ImportError:
            logger.warning(f"BeautifulSoup not available for {filename}, falling back to text")
            return self._parse_text(content, filename)
        except Exception as e:
            logger.warning(f"BeautifulSoup parsing failed for {filename}: {e}")
            return self._parse_text(content, filename)

    def _parse_text(self, content: bytes, filename: str) -> tuple[str, dict[str, Any]]:
        """解析纯文本文件"""
        try:
            text = content.decode("utf-8")
        except UnicodeDecodeError:
            text = content.decode("utf-8", errors="replace")

        return text, {"parser": "text"}
