"""Brand template generator for StyloBridge.

Reverse-engineers a Pandoc-compatible reference template from any
existing corporate .docx file, eliminating manual template creation.
"""

import os
import re
import shutil
from pathlib import Path

from docx import Document
from docx.enum.style import WD_STYLE_TYPE
from docx.shared import Inches, Pt, RGBColor


# Pandoc's expected style names that map to Markdown elements
PANDOC_STYLE_NAMES = {
    "heading": ["Heading 1", "Heading 2", "Heading 3", "Heading 4", "Heading 5", "Heading 6"],
    "body": ["Normal", "Body Text", "First Paragraph"],
    "list": ["List Bullet", "List Number", "List Paragraph"],
    "table": ["Table Grid", "Grid Table Light"],
    "code": ["Source Code"],
    "quote": ["Block Text"],
    "toc": ["TOC Heading", "toc 1", "toc 2", "toc 3"],
}


class StyleAnalyzer:
    """Analyzes corporate .docx files and generates Pandoc reference templates."""

    def analyze_and_create_template(
        self, source_docx: str, template_name: str, template_dir: str
    ) -> str:
        """Read a branded .docx and create a clean Pandoc reference template.

        Args:
            source_docx: Path to the source branded document.
            template_name: Name for the output template (without extension).
            template_dir: Directory to write the template to.

        Returns:
            Path to the generated template file.
        """
        # Validate source path against workspace root
        workspace_root = Path(os.getenv("WORKSPACE_ROOT", "./data")).resolve()
        source_path = Path(source_docx)
        if source_path.is_symlink():
            raise PermissionError("Access denied: Symlinks not allowed.")
        resolved_source = source_path.resolve()
        if not resolved_source.is_relative_to(workspace_root):
            raise PermissionError(
                f"Access denied: Source path '{source_docx}' is outside workspace root."
            )
        if not resolved_source.exists():
            raise FileNotFoundError(f"Source document not found: {source_docx}")

        # Validate template_name: alphanumeric, hyphens, underscores only
        if not re.match(r'^[\w\-]+$', template_name):
            raise ValueError(
                f"Invalid template name '{template_name}': only alphanumeric, hyphens, and underscores allowed."
            )

        template_dir_path = Path(template_dir).resolve()
        output_path = template_dir_path / f"{template_name}.docx"
        # Verify output stays within template_dir
        if not output_path.resolve().is_relative_to(template_dir_path):
            raise PermissionError("Access denied: Template output escapes template directory.")
        output_path.parent.mkdir(parents=True, exist_ok=True)

        # Start with a copy of the source to preserve all embedded resources
        shutil.copy2(str(source_path), str(output_path))

        source_doc = Document(str(source_path))
        template_doc = Document(str(output_path))

        # Extract and apply styles
        self._transfer_page_layout(source_doc, template_doc)
        self._transfer_styles(source_doc, template_doc)
        self._transfer_headers_footers(source_doc, template_doc)
        self._ensure_pandoc_styles(template_doc)
        self._clear_body_content(template_doc)

        template_doc.save(str(output_path))
        return str(output_path)

    def _transfer_page_layout(self, source: Document, target: Document) -> None:
        """Copy page margins, size, and orientation."""
        for section_src, section_tgt in zip(source.sections, target.sections):
            section_tgt.page_width = section_src.page_width
            section_tgt.page_height = section_src.page_height
            section_tgt.orientation = section_src.orientation
            section_tgt.top_margin = section_src.top_margin
            section_tgt.bottom_margin = section_src.bottom_margin
            section_tgt.left_margin = section_src.left_margin
            section_tgt.right_margin = section_src.right_margin

    def _transfer_styles(self, source: Document, target: Document) -> None:
        """Transfer style definitions from source to target document."""
        for style in source.styles:
            if style.type in (
                WD_STYLE_TYPE.PARAGRAPH,
                WD_STYLE_TYPE.CHARACTER,
                WD_STYLE_TYPE.TABLE,
            ):
                self._copy_style(style, target)

    def _copy_style(self, source_style, target: Document) -> None:
        """Copy a single style's properties to the target document."""
        try:
            target_style = target.styles[source_style.name]
        except KeyError:
            try:
                target_style = target.styles.add_style(
                    source_style.name, source_style.type
                )
            except ValueError:
                return

        # Copy font properties if available
        if hasattr(source_style, "font") and source_style.font:
            src_font = source_style.font
            tgt_font = target_style.font

            if src_font.name:
                tgt_font.name = src_font.name
            if src_font.size:
                tgt_font.size = src_font.size
            if src_font.bold is not None:
                tgt_font.bold = src_font.bold
            if src_font.italic is not None:
                tgt_font.italic = src_font.italic
            if src_font.color and src_font.color.rgb:
                tgt_font.color.rgb = src_font.color.rgb

        # Copy paragraph format if available
        if hasattr(source_style, "paragraph_format") and source_style.paragraph_format:
            src_pf = source_style.paragraph_format
            tgt_pf = target_style.paragraph_format

            if src_pf.space_before is not None:
                tgt_pf.space_before = src_pf.space_before
            if src_pf.space_after is not None:
                tgt_pf.space_after = src_pf.space_after
            if src_pf.line_spacing is not None:
                tgt_pf.line_spacing = src_pf.line_spacing
            if src_pf.alignment is not None:
                tgt_pf.alignment = src_pf.alignment
            if src_pf.left_indent is not None:
                tgt_pf.left_indent = src_pf.left_indent
            if src_pf.first_line_indent is not None:
                tgt_pf.first_line_indent = src_pf.first_line_indent

    def _transfer_headers_footers(self, source: Document, target: Document) -> None:
        """Transfer header/footer content including logos and text."""
        for section_src, section_tgt in zip(source.sections, target.sections):
            # Transfer header
            if section_src.header and section_src.header.is_linked_to_previous is False:
                src_header = section_src.header
                tgt_header = section_tgt.header
                tgt_header.is_linked_to_previous = False

                # Copy header XML for full fidelity (images, formatting)
                for element in list(tgt_header._element):
                    tgt_header._element.remove(element)
                for element in src_header._element:
                    from copy import deepcopy
                    tgt_header._element.append(deepcopy(element))

            # Transfer footer
            if section_src.footer and section_src.footer.is_linked_to_previous is False:
                src_footer = section_src.footer
                tgt_footer = section_tgt.footer
                tgt_footer.is_linked_to_previous = False

                for element in list(tgt_footer._element):
                    tgt_footer._element.remove(element)
                for element in src_footer._element:
                    from copy import deepcopy
                    tgt_footer._element.append(deepcopy(element))

    def _ensure_pandoc_styles(self, doc: Document) -> None:
        """Ensure all Pandoc-expected styles exist in the template.

        If a required style is missing, create it with sensible defaults
        that inherit from the document's existing styling.
        """
        existing_names = {s.name for s in doc.styles}

        for category, names in PANDOC_STYLE_NAMES.items():
            for name in names:
                if name not in existing_names:
                    try:
                        if category == "table":
                            doc.styles.add_style(name, WD_STYLE_TYPE.TABLE)
                        else:
                            doc.styles.add_style(name, WD_STYLE_TYPE.PARAGRAPH)
                    except ValueError:
                        pass  # Style already exists under a different case

    def _clear_body_content(self, doc: Document) -> None:
        """Remove body content while preserving styles, headers, and footers.

        A Pandoc reference doc should be a clean template with styled
        definitions but no actual content.
        """
        body = doc.element.body
        # Keep section properties (which contain header/footer references)
        # Remove all paragraphs and tables from the body
        for child in list(body):
            if child.tag.endswith("}sectPr"):
                continue  # Preserve section properties
            body.remove(child)

        # Add a single empty paragraph to keep the document valid
        doc.add_paragraph("")
