# ─── Document Template Renderer ────────────────────────────────────────────────
#
# HTML rendering, template loading, and field mapping.
# Transforms structured document data into rendered HTML via Jinja2 templates.
#
# ─── Imports ───────────────────────────────────────────────────────────────────

import json
from datetime import UTC, datetime
from pathlib import Path

from jinja2 import Environment, FileSystemLoader, select_autoescape


# ─── Default Style ─────────────────────────────────────────────────────────────

DEFAULT_STYLE = {
    # ── Colors ──
    "primary_color": "#2563eb",
    "primary_dark": "#1D4ED8",
    "primary_light": "#DBEAFE",
    "text_primary": "#111827",
    "text_secondary": "#6B7280",
    "text_tertiary": "#9CA3AF",
    "border_color": "#F3F4F6",
    "bg_subtle": "#F9FAFB",
    # ── Typography ──
    "font_family": "Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
    "font_size": "11px",
    # ── Branding ──
    "logo_url": "",
    "show_watermark": False,
    "watermark_text": "PAID",
    # ── Page ──
    "page_format": "A4",
    "orientation": "portrait",
    "margin_top": "20mm",
    "margin_right": "20mm",
    "margin_bottom": "20mm",
    "margin_left": "20mm",
}


# ─── Field Mapping ─────────────────────────────────────────────────────────────

def apply_field_mapping(submission: dict, field_mapping: dict) -> dict:
    """Transform raw submission data into document-ready structure.

    Takes a form submission and a field mapping config, producing a structured
    document data object. This is where free-form form data becomes invoice-ready.

    Args:
        submission: Raw submission dict from the database
        field_mapping: Mapping config defining how form fields → document fields

    Returns:
        Structured document data dict
    """
    data = submission.get("data", {})
    if not data:
        data = {}

    # ── Extract customer info from submission ──
    customer = {
        "name": submission.get("customer_name", ""),
        "email": submission.get("customer_email", ""),
        "phone": submission.get("customer_phone", ""),
    }

    # ── Process line items ──
    line_items = _build_line_items(data, field_mapping.get("line_items", []))

    # ── Calculate totals ──
    subtotal = sum(item.get("amount", 0) for item in line_items)
    tax_rate = field_mapping.get("tax_rate", 0.0)
    tax = round(subtotal * tax_rate, 2)
    total = round(subtotal + tax, 2)

    # ── Build document data ──
    document_data = {
        "from": {
            "name": field_mapping.get("business_name", "") or submission.get("business_name", ""),
            "email": field_mapping.get("business_email", "") or submission.get("business_email", ""),
            "phone": field_mapping.get("business_phone", "") or submission.get("business_phone", ""),
            "address": field_mapping.get("business_address", "") or submission.get("business_address", ""),
            "tax_id": field_mapping.get("tax_id", ""),
        },
        "to": customer,
        "line_items": line_items,
        "subtotal": subtotal,
        "tax_rate": tax_rate,
        "tax": tax,
        "total": total,
        "notes": field_mapping.get("notes", ""),
        "payment_terms": field_mapping.get("payment_terms", ""),
    }

    # ── Map custom fields ──
    for field_name, field_key in field_mapping.get("custom_fields", {}).items():
        document_data[field_name] = data.get(field_key, "")

    # Pass through additional fields for non-invoice document types
    for extra_field in [
        "executive_summary", "scope", "scope_items", "timeline", "terms", "title",
        "transactions", "opening_balance", "total_charges", "total_payments", "closing_balance",
        "period_start", "period_end",
        "tasks", "time_in", "time_out", "total_hours", "work_order_number", "service_address", "scheduled_date",
        "po_number", "order_number", "carrier", "tracking_number", "weight",
        "contract_title", "preamble", "terms_sections", "effective_date",
    ]:
        if extra_field in data:
            document_data[extra_field] = data[extra_field]

    # Merge from/to from flat keys if provided
    if "from" in data and isinstance(data["from"], dict):
        document_data["from"].update(data["from"])
    if "to" in data and isinstance(data["to"], dict):
        document_data["to"].update(data["to"])

    return document_data


def _build_line_items(data: dict, mapping: list) -> list:
    """Build line items from form data based on mapping config.

    Mapping format:
    [
        {"description": "field_key", "quantity": "field_key", "unit_price": "field_key"},
        {"description": "field_key", "quantity": "field_key", "unit_price": "field_key"},
    ]

    Each mapping entry produces one line item. Amount is calculated server-side.
    """
    line_items = []
    for item_map in mapping:
        description = data.get(item_map.get("description", ""), "")
        quantity = _safe_float(data.get(item_map.get("quantity", "1"), "1"))
        unit_price = _safe_float(data.get(item_map.get("unit_price", "0"), "0"))
        amount = round(quantity * unit_price, 2)

        if description or amount > 0:
            line_items.append(
                {
                    "description": description,
                    "quantity": quantity,
                    "unit_price": unit_price,
                    "amount": amount,
                }
            )

    return line_items


def _safe_float(value, default=0.0) -> float:
    """Safely parse a float from form data."""
    try:
        return float(value) if value is not None else default
    except (ValueError, TypeError):
        return default


# ─── HTML Rendering ────────────────────────────────────────────────────────────

def render_document_html(document_type: str, layout: str, data: dict, style: dict = None) -> str:
    """Render document data as HTML using Jinja2 templates.

    Args:
        document_type: 'invoice', 'quote', 'receipt', etc.
        layout: 'classic', 'modern', 'receipt', 'statement'
        data: Structured document data from apply_field_mapping()
        style: Style override dict (colors, fonts, etc.)

    Returns:
        Rendered HTML string
    """
    # Resolve template directory
    template_dir = Path(__file__).parent.parent / "templates" / "documents"
    if not template_dir.exists():
        template_dir = Path(__file__).parent.parent / "templates"

    env = Environment(
        loader=FileSystemLoader(str(template_dir)),
        autoescape=select_autoescape(["html", "htm"]),
    )

    # Add utility filters
    env.filters["currency"] = _currency_filter
    env.filters["percent"] = _percent_filter
    env.filters["date_format"] = _date_filter
    env.filters["to_bool"] = lambda v: bool(v)
    env.filters["nl2br"] = lambda v: ("<br>\n").join(str(v).split("\n")) if v else ""
    env.filters["signature_line"] = _signature_line_filter
    env.filters["clause_number"] = _clause_number_filter
    env.filters["uppercase"] = lambda v: str(v).upper() if v else ""

    # Merge default style with overrides
    style = {**DEFAULT_STYLE, **(style or {})}

    # Ensure discount field exists in data
    data.setdefault("discount", 0.0)

    # Determine template file
    template_name = f"{layout}.html"
    try:
        template = env.get_template(template_name)
    except Exception:
        # Fallback to classic layout
        template = env.get_template("classic.html")

    # Build render context
    context = {
        "document_type": document_type,
        "data": data,
        "style": style,
        "now": datetime.now(UTC),
        "title_map": {
            "invoice": "INVOICE",
            "quote": "QUOTE",
            "receipt": "RECEIPT",
            "certificate": "CERTIFICATE",
            "report": "REPORT",
            "proposal": "PROPOSAL",
            "statement": "STATEMENT OF ACCOUNT",
            "work_order": "WORK ORDER",
            "delivery_note": "DELIVERY NOTE",
            "contract": "CONTRACT",
        },
    }

    return template.render(**context)


def _currency_filter(value) -> str:
    """Format value as currency string."""
    try:
        return f"${float(value):,.2f}"
    except (ValueError, TypeError):
        return "$0.00"


def _percent_filter(value) -> str:
    """Format value as percentage."""
    try:
        return f"{float(value) * 100:.1f}%"
    except (ValueError, TypeError):
        return "0.0%"


def _date_filter(value, fmt="%B %d, %Y") -> str:
    """Format date string."""
    if isinstance(value, str):
        try:
            return datetime.fromisoformat(value).strftime(fmt)
        except ValueError:
            return value
    return value if value else ""


def _signature_line_filter(name: str) -> str:
    """Generate a signature line for a named party.

    Used in templates: {{ 'John Doe' | signature_line }}
    Renders as a signature block with a line and name underneath.
    """
    if not name:
        name = "Authorized Signature"
    return name


def _clause_number_filter(index: int, start: int = 1) -> str:
    """Return clause number formatted as legal numbering (1., 2., 3., etc.)."""
    return f"{index + start - 1}. "
