"""
Word Document Processing

Extracts text, tables, and structure from Microsoft Word documents.
Uses python-docx for .docx files.
"""

import logging
import re
from dataclasses import dataclass, field
from pathlib import Path

logger = logging.getLogger(__name__)


@dataclass
class WordTable:
    """Represents a table extracted from a Word document."""

    table_index: int
    headers: list[str]
    rows: list[list[str]]
    caption: str | None = None

    @property
    def num_rows(self) -> int:
        return len(self.rows)

    @property
    def num_cols(self) -> int:
        return len(self.headers) if self.headers else (len(self.rows[0]) if self.rows else 0)

    def to_markdown(self) -> str:
        """Convert table to markdown format."""
        if not self.headers and not self.rows:
            return ""

        lines = []

        if self.caption:
            lines.append(f"**{self.caption}**\n")

        headers = self.headers or ([""] * self.num_cols)
        lines.append("| " + " | ".join(headers) + " |")
        lines.append("| " + " | ".join(["---"] * len(headers)) + " |")

        for row in self.rows:
            padded_row = row + [""] * (len(headers) - len(row))
            lines.append("| " + " | ".join(padded_row[:len(headers)]) + " |")

        return "\n".join(lines)


@dataclass
class WordSection:
    """Represents a section in a Word document."""

    title: str
    level: int  # Heading level (1-9)
    content: str = ""
    paragraph_index: int = 0


@dataclass
class WordExtractionResult:
    """Result of Word document extraction."""

    file_path: Path
    text_content: str
    sections: list[WordSection] = field(default_factory=list)
    tables: list[WordTable] = field(default_factory=list)
    metadata: dict = field(default_factory=dict)
    total_paragraphs: int = 0
    total_tables: int = 0

    @property
    def has_tables(self) -> bool:
        return len(self.tables) > 0


class WordExtractor:
    """
    Extracts content from Word documents (.docx).

    Features:
    - Full text extraction with structure preservation
    - Table extraction with header detection
    - Section/heading hierarchy extraction
    - Document metadata extraction

    Example:
        extractor = WordExtractor()
        result = extractor.extract("document.docx")
        print(result.text_content)
        for table in result.tables:
            print(table.to_markdown())
    """

    # Heading style name patterns
    HEADING_PATTERNS = [
        (1, re.compile(r"^heading\s*1$", re.IGNORECASE)),
        (2, re.compile(r"^heading\s*2$", re.IGNORECASE)),
        (3, re.compile(r"^heading\s*3$", re.IGNORECASE)),
        (4, re.compile(r"^heading\s*4$", re.IGNORECASE)),
        (5, re.compile(r"^heading\s*5$", re.IGNORECASE)),
        (6, re.compile(r"^heading\s*6$", re.IGNORECASE)),
        (7, re.compile(r"^heading\s*7$", re.IGNORECASE)),
        (8, re.compile(r"^heading\s*8$", re.IGNORECASE)),
        (9, re.compile(r"^heading\s*9$", re.IGNORECASE)),
        (1, re.compile(r"^title$", re.IGNORECASE)),
        (2, re.compile(r"^subtitle$", re.IGNORECASE)),
    ]

    def __init__(
        self,
        extract_tables: bool = True,
        extract_structure: bool = True,
    ):
        """
        Initialize Word extractor.

        Args:
            extract_tables: Whether to extract tables
            extract_structure: Whether to extract document structure
        """
        self.extract_tables = extract_tables
        self.extract_structure = extract_structure
        self._check_dependencies()

    def _check_dependencies(self):
        """Check that required dependencies are available."""
        try:
            import docx  # noqa: F401
        except ImportError:
            raise ImportError("python-docx not installed. Run: pip install python-docx") from None

    def extract(self, file_path: str | Path) -> WordExtractionResult:
        """
        Extract content from a Word document.

        Args:
            file_path: Path to .docx file

        Returns:
            WordExtractionResult containing extracted content
        """
        from docx import Document

        file_path = Path(file_path)

        if not file_path.exists():
            raise FileNotFoundError(f"Word document not found: {file_path}")

        if not file_path.suffix.lower() == ".docx":
            raise ValueError(f"Unsupported file format: {file_path.suffix}")

        doc = Document(str(file_path))

        # Extract metadata
        metadata = self._extract_metadata(doc)

        # Extract text and structure
        text_content, sections = self._extract_content(doc)

        # Extract tables
        tables = []
        if self.extract_tables:
            tables = self._extract_tables(doc)

        return WordExtractionResult(
            file_path=file_path,
            text_content=text_content,
            sections=sections,
            tables=tables,
            metadata=metadata,
            total_paragraphs=len(doc.paragraphs),
            total_tables=len(doc.tables),
        )

    def _extract_metadata(self, doc) -> dict:
        """Extract document metadata."""
        core_props = doc.core_properties

        return {
            "title": core_props.title or "",
            "author": core_props.author or "",
            "subject": core_props.subject or "",
            "keywords": core_props.keywords or "",
            "created": str(core_props.created) if core_props.created else "",
            "modified": str(core_props.modified) if core_props.modified else "",
            "last_modified_by": core_props.last_modified_by or "",
            "revision": core_props.revision,
            "category": core_props.category or "",
            "comments": core_props.comments or "",
        }

    def _extract_content(self, doc) -> tuple[str, list[WordSection]]:
        """Extract text content and structure."""
        all_text = []
        sections = []

        for i, para in enumerate(doc.paragraphs):
            text = para.text.strip()

            if not text:
                all_text.append("")
                continue

            # Check if this is a heading
            heading_level = self._get_heading_level(para)

            if heading_level and self.extract_structure:
                sections.append(WordSection(
                    title=text,
                    level=heading_level,
                    paragraph_index=i,
                ))
                all_text.append(f"\n{'#' * heading_level} {text}\n")
            else:
                all_text.append(text)

        return "\n\n".join(all_text), sections

    def _get_heading_level(self, paragraph) -> int | None:
        """Determine if paragraph is a heading and its level."""
        style_name = paragraph.style.name if paragraph.style else ""

        for level, pattern in self.HEADING_PATTERNS:
            if pattern.match(style_name):
                return level

        return None

    def _extract_tables(self, doc) -> list[WordTable]:
        """Extract all tables from the document."""
        tables = []

        for idx, table in enumerate(doc.tables):
            if not table.rows:
                continue

            # Extract headers from first row
            headers = []
            if table.rows:
                first_row = table.rows[0]
                headers = [cell.text.strip() for cell in first_row.cells]

            # Extract data rows
            rows = []
            for row in table.rows[1:]:
                row_data = [cell.text.strip() for cell in row.cells]
                rows.append(row_data)

            tables.append(WordTable(
                table_index=idx,
                headers=headers,
                rows=rows,
            ))

        logger.info(f"Extracted {len(tables)} tables")
        return tables


def extract_word(
    file_path: str | Path,
    **kwargs,
) -> WordExtractionResult:
    """
    Convenience function to extract content from a Word document.

    Args:
        file_path: Path to .docx file
        **kwargs: Arguments passed to WordExtractor

    Returns:
        WordExtractionResult
    """
    extractor = WordExtractor(**kwargs)
    return extractor.extract(file_path)
