"""
The signed-contract PDF (GET /contracts/{id}/pdf, and the identical bytes
attached to the contract_signed email) hardcoded "INDELIS Cemetery" as the
letterhead regardless of tenant. Covers the fix.

The online payment link intentionally lives only in the email body (plain
text + clickable HTML), not inside the PDF itself — redundant there since
the PDF is generated once at issue time and the link already reaches the
purchaser via the email.
"""
from __future__ import annotations

import base64
import re
import zlib

import pytest
from httpx import AsyncClient
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from src.apps.billing.models.invoice import Invoice
from src.apps.sales.services.contract_pdf_service import _crest_initials

CONTRACTS_URL = "/api/v1/sales/contracts"
INVOICES_URL = "/api/v1/billing/invoices"


def _headers(token: str, tenant_id) -> dict:
    return {"Authorization": f"Bearer {token}", "X-Tenant-ID": str(tenant_id)}


def _pdf_text(pdf_bytes: bytes) -> str:
    """Decode all content streams in a PDF and return joined text.

    ReportLab wraps streams as ``[/ASCII85Decode /FlateDecode]`` (with the
    ASCII85 EOD marker ``~>`` sometimes butting directly against the
    ``endstream`` keyword, no newline in between) — decode both layers,
    tolerating either being absent, before falling back to raw bytes.
    """
    parts = []
    for m in re.finditer(rb"stream\r?\n(.*?)(?:\r?\n)?endstream", pdf_bytes, re.DOTALL):
        raw = m.group(1)
        if raw.endswith(b"~>"):
            raw = raw[:-2]
        decoded = raw
        try:
            decoded = base64.a85decode(decoded, adobe=False)
        except Exception:
            pass
        try:
            decoded = zlib.decompress(decoded)
        except Exception:
            pass
        text = decoded.decode("latin-1", errors="replace")
        # PDF string literals escape parens/backslashes for syntax reasons
        # (e.g. "HST \(13%\):") — unescape so substring assertions read naturally.
        text = re.sub(r"\\([()\\])", r"\1", text)
        parts.append(text)
    return "\n".join(parts)


async def _create_contract(client: AsyncClient, token: str, tenant_id) -> dict:
    body = {
        "contract_type": "pre_need",
        "purchaser_name": "Mary McLeod",
        "purchaser_email": "buyer@example.ca",
        "payment_plan_type": "full",
        "line_items": [
            {"description": "Burial plot A-12", "quantity": 1, "unit_price": "3000.00"},
        ],
    }
    resp = await client.post(CONTRACTS_URL, json=body, headers=_headers(token, tenant_id))
    assert resp.status_code == 201, resp.text
    return resp.json()["data"]


async def _sign_contract(client: AsyncClient, token: str, tenant_id, contract_id: str) -> None:
    fake_sig = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="
    resp = await client.post(
        f"{CONTRACTS_URL}/{contract_id}/sign",
        json={"purchaser_signature_b64": fake_sig},
        headers=_headers(token, tenant_id),
    )
    assert resp.status_code == 200, resp.text


async def _issue_contract(client: AsyncClient, token: str, tenant_id, contract_id: str) -> dict:
    resp = await client.patch(
        f"{CONTRACTS_URL}/{contract_id}/issue",
        headers=_headers(token, tenant_id),
    )
    assert resp.status_code == 200, resp.text
    return resp.json()["data"]


@pytest.mark.asyncio
async def test_issued_contract_pdf_omits_payment_link(
    client: AsyncClient, admin_token: str, test_account,
):
    """The payment link belongs in the email only, not baked into the PDF."""
    contract = await _create_contract(client, admin_token, test_account.id)
    await _sign_contract(client, admin_token, test_account.id, contract["id"])
    issued = await _issue_contract(client, admin_token, test_account.id, contract["id"])
    payment_url = issued["payment_link_url"]
    assert payment_url

    resp = await client.get(
        f"{CONTRACTS_URL}/{contract['id']}/pdf",
        headers=_headers(admin_token, test_account.id),
    )
    assert resp.status_code == 200
    assert resp.headers["content-type"] == "application/pdf"
    assert payment_url.encode() not in resp.content


@pytest.mark.asyncio
async def test_issued_contract_pdf_uses_tenant_cemetery_name(
    client: AsyncClient, admin_token: str, test_account,
):
    """test_account.organization_name is "Test Cemetery" — the PDF must show
    that, not the hardcoded platform-level "INDELIS Cemetery" string."""
    contract = await _create_contract(client, admin_token, test_account.id)
    await _sign_contract(client, admin_token, test_account.id, contract["id"])
    await _issue_contract(client, admin_token, test_account.id, contract["id"])

    resp = await client.get(
        f"{CONTRACTS_URL}/{contract['id']}/pdf",
        headers=_headers(admin_token, test_account.id),
    )
    assert resp.status_code == 200
    text = _pdf_text(resp.content)
    assert test_account.organization_name in text
    # The closing legal paragraph also used to hardcode "INDELIS Cemetery".
    assert "and Test Cemetery upon execution" in text
    # Letterhead crest badge mirrors the wizard preview's initials logic.
    assert "TC" in text


@pytest.mark.asyncio
async def test_issued_contract_pdf_has_blank_boxes_and_witness_text(
    client: AsyncClient, admin_token: str, test_account,
):
    """Signature boxes are a surface to sign on, not a data display — no
    "To be signed by purchaser" placeholder or witness name inside them.
    witness_name (persisted at create time — the real wizard flow never
    calls /sign) prints as plain text below instead."""
    body = {
        "contract_type": "pre_need",
        "purchaser_name": "Mary McLeod",
        "purchaser_email": "buyer@example.ca",
        "payment_plan_type": "full",
        "witness_name": "Jordan Ellis",
        "line_items": [
            {"description": "Burial plot A-12", "quantity": 1, "unit_price": "3000.00"},
        ],
    }
    resp = await client.post(CONTRACTS_URL, json=body, headers=_headers(admin_token, test_account.id))
    assert resp.status_code == 201, resp.text
    contract = resp.json()["data"]
    assert contract["witness_name"] == "Jordan Ellis"

    # Matches the real wizard flow: create -> issue directly, no /sign call.
    issue_resp = await client.patch(
        f"{CONTRACTS_URL}/{contract['id']}/issue",
        headers=_headers(admin_token, test_account.id),
    )
    assert issue_resp.status_code == 200, issue_resp.text

    resp = await client.get(
        f"{CONTRACTS_URL}/{contract['id']}/pdf",
        headers=_headers(admin_token, test_account.id),
    )
    assert resp.status_code == 200
    text = _pdf_text(resp.content)
    assert "To be signed by purchaser" not in text
    assert "Jordan Ellis" in text


@pytest.mark.asyncio
async def test_sign_does_not_clobber_witness_name_set_at_create(
    client: AsyncClient, admin_token: str, test_account,
):
    """A /sign call that omits witness_name must not blank out a name
    already recorded at create time."""
    body = {
        "contract_type": "pre_need",
        "purchaser_name": "Mary McLeod",
        "purchaser_email": "buyer@example.ca",
        "payment_plan_type": "full",
        "witness_name": "Jordan Ellis",
        "line_items": [
            {"description": "Burial plot A-12", "quantity": 1, "unit_price": "3000.00"},
        ],
    }
    resp = await client.post(CONTRACTS_URL, json=body, headers=_headers(admin_token, test_account.id))
    contract = resp.json()["data"]

    await _sign_contract(client, admin_token, test_account.id, contract["id"])

    get_resp = await client.get(
        f"{CONTRACTS_URL}/{contract['id']}",
        headers=_headers(admin_token, test_account.id),
    )
    assert get_resp.json()["data"]["witness_name"] == "Jordan Ellis"


def test_crest_initials_matches_wizard_preview_logic():
    assert _crest_initials("Green Hills Cemetery") == "GH"
    assert _crest_initials("Test Cemetery") == "TC"
    assert _crest_initials("Riverside") == "R"
    assert _crest_initials("") == "C"


# ── The contract PDF is now a simple agreement only; billing detail moved ────
# to its own Invoice PDF (both attached to the contract_signed email).

@pytest.mark.asyncio
async def test_issued_contract_pdf_has_no_billing_detail(
    client: AsyncClient, admin_token: str, test_account,
):
    """Meta table / Purchaser Information / Items table moved to the
    invoice PDF — the contract PDF only has the simple agreement fields."""
    contract = await _create_contract(client, admin_token, test_account.id)
    await _sign_contract(client, admin_token, test_account.id, contract["id"])
    await _issue_contract(client, admin_token, test_account.id, contract["id"])

    resp = await client.get(
        f"{CONTRACTS_URL}/{contract['id']}/pdf",
        headers=_headers(admin_token, test_account.id),
    )
    assert resp.status_code == 200
    text = _pdf_text(resp.content)
    assert "Purchaser Information" not in text
    assert "Burial plot A-12" not in text
    assert "Purchaser:" in text
    assert "Total payable:" in text


@pytest.mark.asyncio
async def test_issued_contract_invoice_pdf_has_billing_detail_and_branding(
    client: AsyncClient, admin_token: str, test_account, db_session: AsyncSession,
):
    """The separate invoice PDF carries the header, purchaser info, and
    itemized line items — with real cemetery branding, not the hardcoded
    platform-subscription-invoice defaults ("INDELIS Subscription")."""
    contract = await _create_contract(client, admin_token, test_account.id)
    await _sign_contract(client, admin_token, test_account.id, contract["id"])
    await _issue_contract(client, admin_token, test_account.id, contract["id"])

    invoice = (
        await db_session.execute(
            select(Invoice).where(Invoice.contract_id == contract["id"])
        )
    ).scalar_one()

    resp = await client.get(
        f"{INVOICES_URL}/{invoice.id}/pdf",
        headers=_headers(admin_token, test_account.id),
    )
    assert resp.status_code == 200
    assert resp.headers["content-type"] == "application/pdf"
    text = _pdf_text(resp.content)
    assert test_account.organization_name.upper() in text
    assert "INDELIS Subscription" not in text
    assert "Purchaser Information" in text
    assert "Burial plot A-12" in text
    # Items table carries its own Subtotal/HST/Total breakdown (line items
    # are $3000 flat with no tax applied at creation, so HST is computed here).
    assert "Subtotal:" in text
    assert "HST (13%):" in text
    assert "$390.00" in text  # 13% of $3,000.00
    assert "$3,390.00" in text  # grand total incl. HST
