#!/usr/bin/env python3
"""End-to-end PDF test: generate a document for every layout and verify output.

Tests cover:
- All template layouts render without errors
- All document types (invoice, proposal, statement, work_order, delivery_note, contract)
- Field mapping transforms raw submission data correctly
- PDF generation returns valid PDF bytes (proper header)
- Logo variants (with/without logo) don't break rendering
"""

import base64
import json
import os
import sys
from datetime import datetime
from pathlib import Path

import pytest

# Ensure we can import app modules
sys.path.insert(0, str(Path(__file__).parent.parent))

from app.services.documents import (
    render_document_html,
    generate_pdf,
    TEMPLATE_LAYOUTS,
    DEFAULT_STYLE,
    DOCUMENT_TYPES,
    apply_field_mapping,
)
from app.services.document_builders import (
    build_invoice_data,
    build_proposal_data,
    build_statement_data,
    build_work_order_data,
    build_delivery_note_data,
    build_contract_data,
)

# ─── Minimal test data fixtures ───────────────────────────────────────────────

SAMPLE_DATA = {
    "document_number": "INV-2026-0001",
    "issue_date": "2026-06-15",
    "due_date": "2026-07-15",
    "from": {
        "name": "Acme Creative Studio",
        "email": "billing@acmestudio.io",
        "phone": "+1 (555) 012-3456",
        "address": "123 Design Lane\nSan Francisco, CA 94102",
        "tax_id": "US-12-3456789",
    },
    "to": {
        "name": "Global Tech Corp",
        "email": "ap@globaltech.com",
        "phone": "+1 (555) 987-6543",
        "address": "456 Enterprise Blvd\nAustin, TX 78701",
    },
    "line_items": [
        {
            "description": "Brand Identity Design",
            "quantity": 1,
            "unit_price": 4500.00,
            "amount": 4500.00,
        },
        {
            "description": "Website Design & Development",
            "quantity": 1,
            "unit_price": 8200.00,
            "amount": 8200.00,
        },
        {
            "description": "Monthly Content Strategy",
            "quantity": 3,
            "unit_price": 750.00,
            "amount": 2250.00,
        },
    ],
    "subtotal": 14950.00,
    "tax_rate": 0.0825,
    "tax": 1233.38,
    "total": 16183.38,
    "currency": "USD",
    "payment_terms": "Net 30",
    "notes": "Please reference invoice #INV-2026-0001 on all payments.",
    "payment_link": "https://pay.example.com/INV-2026-0001",
    "payment_link_text": "Pay Now",
    "status": "pending",
    "discount": 0.0,
}

SAMPLE_STYLE = {
    **DEFAULT_STYLE,
    "primary_color": "#2563eb",
    "logo_url": "",
}

SAMPLE_STYLE_WITH_LOGO = {
    **SAMPLE_STYLE,
    "logo_url": "data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='120' height='40' viewBox='0 0 120 40'><rect fill='%232563eb' width='120' height='40' rx='4'/><text x='60' y='25' text-anchor='middle' fill='white' font-size='16' font-family='sans-serif' font-weight='bold'>ACME</text></svg>",
}


# ─── Helper ───────────────────────────────────────────────────────────────────

def assert_valid_pdf(pdf_bytes: bytes):
    """Assert that bytes are a valid PDF."""
    assert pdf_bytes, "PDF bytes are empty"
    assert len(pdf_bytes) > 100, f"PDF too small ({len(pdf_bytes)} bytes) — likely broken"
    assert pdf_bytes[:5] == b"%PDF-", f"Invalid PDF header: {pdf_bytes[:20]!r}"


# ─── Test: All layouts render ────────────────────────────────────────────────

class TestAllLayouts:
    """Test that every template layout renders and generates valid PDFs."""

    @pytest.mark.parametrize("layout", TEMPLATE_LAYOUTS)
    def test_layout_renders(self, layout):
        """Each layout template renders HTML without error."""
        html = render_document_html("invoice", layout, SAMPLE_DATA, SAMPLE_STYLE)
        assert html, f"Layout '{layout}' produced empty HTML"
        assert len(html) > 100, f"Layout '{layout}' produced suspiciously short HTML"
        assert "<html" in html.lower() or "<!doctype" in html.lower(), (
            f"Layout '{layout}' HTML doesn't look like HTML"
        )

    @pytest.mark.parametrize("layout", TEMPLATE_LAYOUTS)
    def test_layout_pdf_valid(self, layout):
        """Each layout generates valid PDF bytes."""
        html = render_document_html("invoice", layout, SAMPLE_DATA, SAMPLE_STYLE)
        pdf_bytes = generate_pdf(html, SAMPLE_STYLE)
        assert_valid_pdf(pdf_bytes)


# ─── Test: Document type rendering ────────────────────────────────────────────

class TestDocumentTypes:
    """Test that each document type renders with appropriate content."""

    @pytest.fixture
    def doc_data(self):
        """Base document data shared across document type tests."""
        return SAMPLE_DATA.copy()

    def test_invoice_rendering(self, doc_data):
        """Invoice renders with expected fields."""
        html = render_document_html("invoice", "classic", doc_data, SAMPLE_STYLE)
        assert "INVOICE" in html.upper() or "invoice" in html.lower()
        assert "Acme Creative Studio" in html
        assert "Global Tech Corp" in html
        assert "4500" in html or "4,500" in html  # Line item price

    def test_proposal_rendering(self, doc_data):
        """Proposal renders with expected proposal-specific fields."""
        proposal_data = {
            **doc_data,
            "executive_summary": "We propose a comprehensive digital transformation.",
            "scope_of_work": [
                {"title": "Phase 1: Discovery", "description": "Requirements gathering"},
                {"title": "Phase 2: Design", "description": "UI/UX design"},
            ],
            "timeline": [
                {"phase": "Discovery", "start": "2026-07-01", "end": "2026-07-14"},
            ],
            "terms_conditions": "Proposal valid for 30 days.",
        }
        html = render_document_html("proposal", "proposal", proposal_data, SAMPLE_STYLE)
        assert html, "Proposal HTML is empty"
        assert_valid_pdf(generate_pdf(html, SAMPLE_STYLE))

    def test_statement_rendering(self, doc_data):
        """Statement renders with transaction history."""
        statement_data = {
            **doc_data,
            "opening_balance": 1000.00,
            "period_start": "2026-06-01",
            "period_end": "2026-06-30",
            "transactions": [
                {"date": "2026-06-05", "description": "Payment received", "debit": 0, "credit": 500.00, "reference": "PAY-001"},
                {"date": "2026-06-15", "description": "Invoice paid", "debit": 300.00, "credit": 0, "reference": "INV-001"},
            ],
            "closing_balance": 1200.00,
        }
        html = render_document_html("statement", "statement", statement_data, SAMPLE_STYLE)
        assert html, "Statement HTML is empty"
        assert_valid_pdf(generate_pdf(html, SAMPLE_STYLE))

    def test_work_order_rendering(self, doc_data):
        """Work order renders with job details."""
        wo_data = {
            **doc_data,
            "work_order_number": "WO-2026-0042",
            "job_description": "HVAC maintenance and repair",
            "scheduled_date": "2026-07-15",
            "priority": "High",
            "job_category": "Maintenance",
            "technician": {
                "name": "John Smith",
                "phone": "+1 (555) 111-2222",
                "license": "TECH-12345",
            },
            "tasks": [
                {"title": "Inspect HVAC unit", "completed": False},
                {"title": "Replace air filter", "completed": True},
            ],
            "materials": [
                {"description": "Air filter 20x20x1", "quantity": 2, "unit_price": 25.00},
            ],
            "time_in": "09:00",
            "time_out": "11:30",
            "labor_hours": 2.5,
            "labor_rate": 85.00,
            "signature_required": True,
        }
        html = render_document_html("work_order", "work_order", wo_data, SAMPLE_STYLE)
        assert html, "Work order HTML is empty"
        assert "WO-2026-0042" in html or "Acme Creative Studio" in html
        assert_valid_pdf(generate_pdf(html, SAMPLE_STYLE))

    def test_delivery_note_rendering(self, doc_data):
        """Delivery note renders with shipping info."""
        dn_data = {
            **doc_data,
            "delivery_number": "DN-2026-0150",
            "delivery_date": "2026-07-10",
            "po_number": "PO-2026-0088",
            "carrier": "FedEx Ground",
            "tracking_number": "FX123456789",
            "delivery_address": "789 Warehouse Rd, Austin, TX 78701",
            "items": [
                {"description": "Widget A", "sku": "WGT-A-001", "quantity": 10, "unit": "ea"},
                {"description": "Widget B", "sku": "WGT-B-002", "quantity": 5, "unit": "ea"},
            ],
            "total_items": 15,
            "signature_required": True,
        }
        html = render_document_html("delivery_note", "delivery_note", dn_data, SAMPLE_STYLE)
        assert html, "Delivery note HTML is empty"
        assert "FedEx" in html
        assert_valid_pdf(generate_pdf(html, SAMPLE_STYLE))

    def test_contract_rendering(self, doc_data):
        """Contract renders with parties and clauses."""
        contract_data = {
            **doc_data,
            "contract_number": "CTR-2026-0001",
            "contract_title": "Software Development Agreement",
            "effective_date": "2026-07-01",
            "expiration_date": "2027-06-30",
            "preamble": "This Agreement is entered into by and between the parties below.",
            "party_a": {
                "name": "Acme Creative Studio",
                "email": "billing@acmestudio.io",
                "phone": "+1 (555) 012-3456",
                "address": "123 Design Lane, San Francisco, CA 94102",
                "title": "Provider",
            },
            "party_b": {
                "name": "Global Tech Corp",
                "email": "ap@globaltech.com",
                "phone": "+1 (555) 987-6543",
                "address": "456 Enterprise Blvd, Austin, TX 78701",
                "title": "Client",
            },
            "clauses": [
                {"title": "Scope of Services", "content": "Provider shall develop software as specified."},
                {"title": "Payment Terms", "content": "Client shall pay within 30 days of invoice."},
                {"title": "Confidentiality", "content": "Both parties agree to maintain confidentiality."},
                {"title": "Termination", "content": "Either party may terminate with 60 days notice."},
            ],
            "financial_terms": {
                "amount": 150000.00,
                "currency": "USD",
                "payment_schedule": "Monthly installments",
            },
            "governing_law": "State of California",
            "signatures": {
                "party_a_name": "Acme Creative Studio",
                "party_a_title": "Provider",
                "party_b_name": "Global Tech Corp",
                "party_b_title": "Client",
            },
        }
        html = render_document_html("contract", "contract", contract_data, SAMPLE_STYLE)
        assert html, "Contract HTML is empty"
        assert "Software Development Agreement" in html
        assert_valid_pdf(generate_pdf(html, SAMPLE_STYLE))


# ─── Test: Field mapping ──────────────────────────────────────────────────────

class TestFieldMapping:
    """Test that apply_field_mapping transforms raw data correctly."""

    def test_basic_field_mapping(self):
        """Basic field mapping extracts customer info and line items."""
        submission = {
            "customer_name": "Test Customer",
            "customer_email": "test@example.com",
            "customer_phone": "+1 555 000 0000",
            "data": {
                "item_desc": "Consulting service",
                "item_qty": "10",
                "item_price": "150",
            },
        }
        field_mapping = {
            "business_name": "My Business",
            "business_email": "info@mybusiness.com",
            "business_phone": "+1 555 111 2222",
            "business_address": "1 Business St",
            "tax_rate": 0.10,
            "line_items": [
                {"description": "item_desc", "quantity": "item_qty", "unit_price": "item_price"},
            ],
            "payment_terms": "Net 15",
        }

        result = apply_field_mapping(submission, field_mapping)

        assert result["from"]["name"] == "My Business"
        assert result["from"]["email"] == "info@mybusiness.com"
        assert result["to"]["name"] == "Test Customer"
        assert result["to"]["email"] == "test@example.com"
        assert result["to"]["phone"] == "+1 555 000 0000"
        assert len(result["line_items"]) == 1
        assert result["line_items"][0]["description"] == "Consulting service"
        assert result["line_items"][0]["quantity"] == 10.0
        assert result["line_items"][0]["unit_price"] == 150.0
        assert result["line_items"][0]["amount"] == 1500.0
        assert result["subtotal"] == 1500.0
        assert result["tax"] == 150.0
        assert result["total"] == 1650.0
        assert result["payment_terms"] == "Net 15"

    def test_empty_field_mapping(self):
        """Empty field mapping produces valid but empty document data."""
        submission = {"data": {}}
        result = apply_field_mapping(submission, {})

        assert "from" in result
        assert "to" in result
        assert "line_items" in result
        assert result["line_items"] == []
        assert result["subtotal"] == 0
        assert result["total"] == 0

    def test_custom_fields_mapping(self):
        """Custom fields are passed through correctly."""
        submission = {
            "data": {
                "custom_field_1": "value1",
                "custom_field_2": "value2",
                "item_desc": "Widget",
                "item_qty": "1",
                "item_price": "10",
            },
        }
        field_mapping = {
            "line_items": [
                {"description": "item_desc", "quantity": "item_qty", "unit_price": "item_price"},
            ],
            "custom_fields": {
                "custom_1": "custom_field_1",
                "custom_2": "custom_field_2",
            },
        }

        result = apply_field_mapping(submission, field_mapping)
        assert result["custom_1"] == "value1"
        assert result["custom_2"] == "value2"

    def test_from_to_dict_merge(self):
        """from/to dict fields in data merge with base mapping."""
        submission = {
            "data": {
                "from": {
                    "name": "Override Business",
                    "tax_id": "US-99-9999999",
                },
                "to": {
                    "phone": "+1 555 999 8888",
                },
            },
        }
        field_mapping = {
            "business_name": "Default Business",
        }

        result = apply_field_mapping(submission, field_mapping)
        # from dict in data overrides the mapped value
        assert result["from"]["name"] == "Override Business"
        assert result["from"]["tax_id"] == "US-99-9999999"
        assert result["to"]["phone"] == "+1 555 999 8888"


# ─── Test: Data builders ──────────────────────────────────────────────────────

class TestDataBuilders:
    """Test the build_*_data functions produce correct structured data."""

    def test_build_invoice_data(self):
        """build_invoice_data produces complete invoice structure."""
        template = {
            "id": 1,
            "field_mapping": {"business_name": "Test Corp", "business_email": "test@corp.com"},
            "style_config": DEFAULT_STYLE,
        }
        submission = {
            "customer_name": "Client Co",
            "customer_email": "client@co.com",
            "tax_rate": 8.5,
        }
        line_items = [
            {"description": "Service", "quantity": 2, "unit_price": 100},
        ]

        result = build_invoice_data(template, submission, line_items, "USD")

        assert result["document_type"] == "Invoice"
        assert result["from"]["name"] == "Test Corp"
        assert result["to"]["name"] == "Client Co"
        assert len(result["line_items"]) == 1
        assert result["line_items"][0]["amount"] == 200.0
        assert result["subtotal"] == 200.0
        assert result["tax_amount"] == pytest.approx(17.0)  # 200 * 8.5 / 100
        assert result["total"] == pytest.approx(217.0)

    def test_build_proposal_data(self):
        """build_proposal_data produces complete proposal structure."""
        template = {
            "field_mapping": {"business_name": "Agency Inc"},
        }
        submission = {
            "customer_name": "Prospect Co",
            "executive_summary": "We can help you.",
            "timeline": [{"phase": "Design", "start": "2026-07-01"}],
        }
        line_items = [
            {"description": "Design package", "quantity": 1, "unit_price": 5000},
        ]

        result = build_proposal_data(template, submission, line_items, "USD")

        assert result["document_type"] == "proposal"
        assert result["executive_summary"] == "We can help you."
        assert len(result["timeline"]) == 1
        assert result["from"]["name"] == "Agency Inc"

    def test_build_statement_data(self):
        """build_statement_data calculates running balance correctly."""
        template = {"field_mapping": {"business_name": "Bank Corp"}}
        submission = {
            "customer_name": "Account Holder",
            "opening_balance": 1000,
            "period_start": "2026-06-01",
            "period_end": "2026-06-30",
        }
        transactions = [
            {"date": "2026-06-05", "description": "Deposit", "debit": 0, "credit": 500, "reference": "D001"},
            {"date": "2026-06-15", "description": "Withdrawal", "debit": 200, "credit": 0, "reference": "W001"},
        ]

        result = build_statement_data(template, submission, transactions, "USD")

        assert result["document_type"] == "statement"
        assert len(result["transactions"]) == 2
        # Running balance: 1000 + 500 - 200 = 1300
        assert result["transactions"][1]["balance"] == 1300.0
        assert result["closing_balance"] == 1300.0

    def test_build_work_order_data(self):
        """build_work_order_data includes technician and task info."""
        template = {"field_mapping": {"business_name": "Service Co"}}
        submission = {
            "customer_name": "Site Owner",
            "customer_phone": "+1 555 123 4567",
            "job_description": "AC repair",
            "scheduled_date": "2026-07-15",
            "priority": "Urgent",
            "technician": {"name": "Tech John", "phone": "+1 555 999 0000", "license": "LIC-001"},
            "tasks": [{"title": "Diagnose issue", "completed": True}],
        }

        result = build_work_order_data(template, submission, [], "USD")

        assert result["document_type"] == "work_order"
        assert result["technician"]["name"] == "Tech John"
        assert result["job_description"] == "AC repair"
        assert result["priority"] == "Urgent"
        assert len(result["tasks"]) == 1

    def test_build_delivery_note_data(self):
        """build_delivery_note_data includes shipping info."""
        template = {"field_mapping": {"business_name": "Ship Co"}}
        submission = {
            "customer_name": "Receiver Inc",
            "delivery_address": "123 Ship St",
            "carrier": "UPS",
            "tracking_number": "1Z999AA10123456784",
        }
        line_items = [
            {"description": "Box of widgets", "sku": "BX-001", "quantity": 5, "unit": "box", "weight": "10kg"},
        ]

        result = build_delivery_note_data(template, submission, line_items, "USD")

        assert result["document_type"] == "delivery_note"
        assert result["carrier"] == "UPS"
        assert len(result["items"]) == 1
        assert result["items"][0]["sku"] == "BX-001"
        assert result["total_items"] == 5.0

    def test_build_contract_data(self):
        """build_contract_data includes parties and clauses."""
        template = {"field_mapping": {"business_name": "Provider LLC"}}
        submission = {
            "customer_name": "Client Corp",
            "contract_title": "Service Agreement",
            "effective_date": "2026-07-01",
            "expiration_date": "2027-06-30",
            "clauses": [
                {"title": "Scope", "content": "Provider delivers services."},
                {"title": "Payment", "content": "Client pays $10k/month."},
            ],
            "contract_amount": 120000,
            "governing_law": "Delaware",
        }

        result = build_contract_data(template, submission, line_items=[], currency="USD")

        assert result["document_type"] == "contract"
        assert result["contract_title"] == "Service Agreement"
        assert result["party_a"]["name"] == "Provider LLC"
        assert result["party_b"]["name"] == "Client Corp"
        assert len(result["clauses"]) == 2
        assert result["financial_terms"]["amount"] == 120000.0
        assert result["governing_law"] == "Delaware"


# ─── Test: PDF generation ─────────────────────────────────────────────────────

class TestPDFGeneration:
    """Test that PDF generation produces valid output."""

    def test_pdf_header_valid(self):
        """Generated PDF starts with %PDF- header."""
        html = render_document_html("invoice", "classic", SAMPLE_DATA, SAMPLE_STYLE)
        pdf_bytes = generate_pdf(html, SAMPLE_STYLE)
        assert pdf_bytes[:5] == b"%PDF-", f"Invalid PDF header: {pdf_bytes[:20]!r}"

    def test_pdf_size_reasonable(self):
        """Generated PDF has reasonable size (> 1KB)."""
        html = render_document_html("invoice", "modern", SAMPLE_DATA, SAMPLE_STYLE)
        pdf_bytes = generate_pdf(html, SAMPLE_STYLE)
        assert len(pdf_bytes) > 1024, f"PDF too small: {len(pdf_bytes)} bytes"

    def test_pdf_with_logo(self):
        """PDF generation works with inline SVG logo."""
        html = render_document_html("invoice", "modern", SAMPLE_DATA, SAMPLE_STYLE_WITH_LOGO)
        pdf_bytes = generate_pdf(html, SAMPLE_STYLE_WITH_LOGO)
        assert_valid_pdf(pdf_bytes)

    def test_pdf_empty_data(self):
        """PDF generation handles minimal/empty data gracefully."""
        minimal_data = {
            "from": {"name": "Empty Corp"},
            "to": {"name": "Empty Client"},
            "line_items": [],
            "subtotal": 0,
            "tax": 0,
            "tax_rate": 0,
            "total": 0,
        }
        html = render_document_html("invoice", "classic", minimal_data, SAMPLE_STYLE)
        pdf_bytes = generate_pdf(html, SAMPLE_STYLE)
        assert_valid_pdf(pdf_bytes)


# ─── Test: Config validation ──────────────────────────────────────────────────

class TestConfigValidation:
    """Test that config validation catches missing env vars."""

    def test_validate_with_all_vars(self):
        """validate() passes when all required vars are set."""
        from app.config import validate

        # Required vars are already set by conftest.py
        validate()  # Should not raise

    def test_required_vars_defined(self):
        """Check that required vars list is properly defined."""
        from app.config import _REQUIRED_VARS, _OPTIONAL_VARS

        assert len(_REQUIRED_VARS) >= 2  # At least SECRET_KEY and ENCRYPTION_KEY
        assert len(_OPTIONAL_VARS) >= 3  # Stripe + Redis

        required_names = {v["name"] for v in _REQUIRED_VARS}
        assert "SECRET_KEY" in required_names
        assert "ENCRYPTION_KEY" in required_names

        optional_names = {v["name"] for v in _OPTIONAL_VARS}
        assert "STRIPE_SECRET_KEY" in optional_names
        assert "REDIS_URL" in optional_names


if __name__ == "__main__":
    pytest.main([__file__, "-v", "--tb=short"])