"""Data models and template emitters for URL-derived brand bundles."""

from __future__ import annotations

import re
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any, Dict, List, Optional

from tokenmoulds.dtcg.generator import TokenGenerator
from tokenmoulds.dtcg.recipe import RecipeInputs
from tokenmoulds.emitters import PowerPointEmitter, WordDocumentEmitter
from tokenmoulds.ir import build_document_ir


@dataclass
class ExtractedBrand:
    """Brand values extracted from a website."""

    url: str
    name: str

    # Colors
    primary_color: str
    secondary_color: str
    text_color: str = "#2B2B2B"
    background_color: str = "#FFFFFF"

    # Fonts
    heading_font: str = "Georgia"
    body_font: str = "Arial"

    # Extracted metadata
    all_colors: Dict[str, str] = field(default_factory=dict)
    all_fonts: List[str] = field(default_factory=list)

    # Inferred parameters
    locale: str = "US"
    brand_tone: float = 0.5
    type_scale_ratio: float = 1.25


@dataclass
class BrandTemplates:
    """Generated brand templates ready to save."""

    brand: ExtractedBrand
    recipe_inputs: RecipeInputs
    tokens_json: str

    # Lazy-generated templates
    _word_template: Optional[bytes] = field(default=None, repr=False)
    _word_document: Optional[bytes] = field(default=None, repr=False)
    _powerpoint_template: Optional[bytes] = field(default=None, repr=False)
    _excel_template: Optional[bytes] = field(default=None, repr=False)
    _document_ir: Any = field(default=None, repr=False)

    def _get_powerpoint_template(self) -> bytes:
        """Generate PowerPoint template."""
        if self._powerpoint_template is None:
            ir = self._get_ir()
            emitter = PowerPointEmitter(ir, embed_fonts=False)
            self._powerpoint_template = emitter.build_package()
        return self._powerpoint_template

    def _get_ir(self):
        """Get or create DocumentIR."""
        if self._document_ir is None:
            tokens = TokenGenerator().generate(
                {
                    "font_pair": {
                        "sans": self.recipe_inputs.font_sans,
                        "serif": self.recipe_inputs.font_serif,
                    },
                    "base_colors": {
                        "primary": self.recipe_inputs.primary_color,
                        "secondary": self.recipe_inputs.secondary_color,
                        "accent": self.recipe_inputs.secondary_color,
                    },
                    "locale": self.recipe_inputs.locale,
                    "brand_tone": self.recipe_inputs.brand_tone,
                    "content_density": self.recipe_inputs.content_density,
                    "base_grid_pt": self.recipe_inputs.baseline_pt,
                }
            )
            self._document_ir = build_document_ir(
                tokens, self.brand.name, self.recipe_inputs.locale
            )
        return self._document_ir

    def _get_word_template(self) -> bytes:
        """Generate Word template."""
        if self._word_template is None:
            ir = self._get_ir()
            emitter = WordDocumentEmitter(ir, embed_fonts=False)
            self._word_template = emitter.build_package()
        return self._word_template

    def save_word(self, path: str, as_document: bool = False) -> str:
        """Save Word template (.dotx) or document (.docx).

        Args:
            path: Output file path
            as_document: If True, save as .docx instead of .dotx

        Returns:
            Path to saved file
        """
        template_bytes = self._get_word_template()

        if as_document:
            # Convert template to document
            import io
            import zipfile

            output = io.BytesIO()
            with zipfile.ZipFile(io.BytesIO(template_bytes), "r") as template:
                with zipfile.ZipFile(output, "w", zipfile.ZIP_DEFLATED) as doc:
                    for item in template.namelist():
                        data = template.read(item)
                        if item == "[Content_Types].xml":
                            data = data.replace(
                                b"application/vnd.openxmlformats-officedocument.wordprocessingml.template.main+xml",
                                b"application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml",
                            )
                        doc.writestr(item, data)

            Path(path).write_bytes(output.getvalue())
        else:
            Path(path).write_bytes(template_bytes)

        return path

    def save_powerpoint(self, path: str) -> str:
        """Save PowerPoint template (.potx).

        Args:
            path: Output file path

        Returns:
            Path to saved file
        """
        template_bytes = self._get_powerpoint_template()
        Path(path).write_bytes(template_bytes)
        return path

    def save_excel(self, path: str) -> str:
        """Save Excel template (.xltx).

        Args:
            path: Output file path

        Returns:
            Path to saved file
        """
        from tokenmoulds.emitters import ExcelEmitter

        ir = self._get_ir()
        template_bytes = ExcelEmitter(ir).build_package()
        Path(path).write_bytes(template_bytes)
        return path

    def save_tokens(self, path: str) -> str:
        """Save DTCG token file (.tokens.json).

        Args:
            path: Output file path

        Returns:
            Path to saved file
        """
        Path(path).write_text(self.tokens_json, encoding="utf-8")
        return path

    def save(self, output_dir: str) -> Dict[str, str]:
        """Save all templates to a directory.

        Args:
            output_dir: Output directory path

        Returns:
            Dict mapping format to file path
        """
        out = Path(output_dir)
        out.mkdir(parents=True, exist_ok=True)

        slug = self.brand.name.lower().replace(" ", "-")
        slug = re.sub(r"[^a-z0-9-]", "", slug)

        saved = {}

        # Always save tokens
        saved["tokens"] = self.save_tokens(str(out / f"{slug}.tokens.json"))

        # Save Word template
        try:
            saved["word"] = self.save_word(str(out / f"{slug}.dotx"))
        except Exception as e:
            print(f"Warning: Could not generate Word template: {e}")

        # Save PowerPoint template
        try:
            saved["powerpoint"] = self.save_powerpoint(str(out / f"{slug}.potx"))
        except Exception as e:
            print(f"Warning: Could not generate PowerPoint template: {e}")

        # Save Excel (when implemented)
        try:
            saved["excel"] = self.save_excel(str(out / f"{slug}.xltx"))
        except NotImplementedError:
            pass

        return saved
