"""
Word document builder using python-docx.
Creates formatted Word documents from JSON structure.
"""
import logging
from typing import Dict, Any, List
from io import BytesIO
from docx import Document
from docx.shared import Pt, RGBColor, Inches
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.oxml.ns import qn
from docx.oxml import OxmlElement

logger = logging.getLogger(__name__)


class WordDocumentBuilder:
    """
    Builder for creating Word documents from JSON structure.

    Supports:
    - Headings (levels 1-9)
    - Paragraphs with formatting (bold, italic, underline)
    - Tables with headers and data
    - Bulleted and numbered lists
    - Page breaks
    """

    def __init__(self):
        """Initialize the document builder."""
        self.doc = Document()

    def build_from_json(self, content_list: List[Dict[str, Any]]) -> bytes:
        """
        Build a Word document from JSON content structure.

        Args:
            content_list: List of content items, each with a 'type' and specific properties

        Returns:
            Document content as bytes

        Example content_list:
        [
            {"type": "heading", "level": 1, "text": "Title"},
            {"type": "paragraph", "text": "Some text", "bold": true},
            {"type": "table", "headers": ["Col1", "Col2"], "rows": [["A", "B"]]},
            {"type": "list", "style": "bullet", "items": ["Item 1", "Item 2"]},
            {"type": "page_break"}
        ]
        """
        for item in content_list:
            item_type = item.get("type", "")

            try:
                if item_type == "heading":
                    self._add_heading(item)
                elif item_type == "paragraph":
                    self._add_paragraph(item)
                elif item_type == "table":
                    self._add_table(item)
                elif item_type == "list":
                    self._add_list(item)
                elif item_type == "page_break":
                    self._add_page_break()
                else:
                    logger.warning(f"Unknown content type: {item_type}")

            except Exception as e:
                logger.error(f"Error adding {item_type}: {e}", exc_info=True)
                # Add error message to document
                p = self.doc.add_paragraph()
                run = p.add_run(f"[Error adding {item_type}: {str(e)}]")
                run.font.color.rgb = RGBColor(255, 0, 0)

        # Save to BytesIO
        buffer = BytesIO()
        self.doc.save(buffer)
        buffer.seek(0)
        return buffer.read()

    def _add_heading(self, item: Dict[str, Any]) -> None:
        """
        Add a heading to the document.

        Args:
            item: {"type": "heading", "level": 1-9, "text": "Heading text"}
        """
        level = item.get("level", 1)
        text = item.get("text", "")

        if level < 1 or level > 9:
            logger.warning(f"Invalid heading level {level}, using level 1")
            level = 1

        self.doc.add_heading(text, level=level)

    def _add_paragraph(self, item: Dict[str, Any]) -> None:
        """
        Add a paragraph with optional formatting.

        Args:
            item: {
                "type": "paragraph",
                "text": "Paragraph text",
                "bold": true/false (optional),
                "italic": true/false (optional),
                "underline": true/false (optional),
                "font_size": 12 (optional, in points),
                "align": "left"/"center"/"right" (optional)
            }
        """
        text = item.get("text", "")
        bold = item.get("bold", False)
        italic = item.get("italic", False)
        underline = item.get("underline", False)
        font_size = item.get("font_size")
        align = item.get("align", "left")

        p = self.doc.add_paragraph()
        run = p.add_run(text)

        # Apply formatting
        if bold:
            run.bold = True
        if italic:
            run.italic = True
        if underline:
            run.underline = True
        if font_size:
            run.font.size = Pt(font_size)

        # Apply alignment
        if align == "center":
            p.alignment = WD_ALIGN_PARAGRAPH.CENTER
        elif align == "right":
            p.alignment = WD_ALIGN_PARAGRAPH.RIGHT
        # left is default

    def _add_table(self, item: Dict[str, Any]) -> None:
        """
        Add a table to the document.

        Args:
            item: {
                "type": "table",
                "headers": ["Col1", "Col2", "Col3"],
                "rows": [
                    ["Data1", "Data2", "Data3"],
                    ["Data4", "Data5", "Data6"]
                ],
                "style": "Light Grid Accent 1" (optional)
            }
        """
        headers = item.get("headers", [])
        rows = item.get("rows", [])
        style = item.get("style", "Light Grid Accent 1")

        if not headers:
            logger.warning("Table has no headers, skipping")
            return

        # Create table with header row + data rows
        table = self.doc.add_table(rows=1 + len(rows), cols=len(headers))

        # Apply style
        try:
            table.style = style
        except Exception as e:
            logger.warning(f"Could not apply table style '{style}': {e}")

        # Add headers
        header_row = table.rows[0]
        for i, header_text in enumerate(headers):
            cell = header_row.cells[i]
            cell.text = str(header_text)
            # Make header bold
            for paragraph in cell.paragraphs:
                for run in paragraph.runs:
                    run.bold = True

        # Add data rows
        for row_idx, row_data in enumerate(rows, start=1):
            table_row = table.rows[row_idx]
            for col_idx, cell_data in enumerate(row_data):
                if col_idx < len(headers):  # Don't exceed column count
                    table_row.cells[col_idx].text = str(cell_data)

    def _add_list(self, item: Dict[str, Any]) -> None:
        """
        Add a bulleted or numbered list to the document.

        Args:
            item: {
                "type": "list",
                "style": "bullet" or "number",
                "items": ["Item 1", "Item 2", "Item 3"]
            }
        """
        style = item.get("style", "bullet")
        items = item.get("items", [])

        if not items:
            logger.warning("List has no items, skipping")
            return

        for item_text in items:
            if style == "number":
                self.doc.add_paragraph(str(item_text), style='List Number')
            else:  # bullet
                self.doc.add_paragraph(str(item_text), style='List Bullet')

    def _add_page_break(self) -> None:
        """Add a page break to the document."""
        self.doc.add_page_break()


def create_word_document(content: List[Dict[str, Any]]) -> bytes:
    """
    Create a Word document from JSON content structure.

    Args:
        content: List of content items (headings, paragraphs, tables, lists, page breaks)

    Returns:
        Document bytes ready for upload

    Example:
        content = [
            {"type": "heading", "level": 1, "text": "Monthly Report"},
            {"type": "paragraph", "text": "January 2025"},
            {"type": "table",
             "headers": ["Task", "Status", "Notes"],
             "rows": [
                 ["Task 1", "Complete", "Done"],
                 ["Task 2", "In Progress", "Working"]
             ]},
            {"type": "page_break"},
            {"type": "heading", "level": 2, "text": "Next Steps"},
            {"type": "list", "style": "bullet", "items": ["Step 1", "Step 2"]}
        ]
        doc_bytes = create_word_document(content)
    """
    builder = WordDocumentBuilder()
    return builder.build_from_json(content)
