"""
Tests for GET /api/public/contracts/{subdomain}/{contract_number}/pdf —
the unauthenticated contract-PDF redirect used by the contract QR code
(a scanning phone can't send the X-Tenant-Subdomain header the SPA injects,
so the tenant is resolved from the subdomain path segment instead).

Covers:
  - 404 for an unknown subdomain
  - 404 for a known tenant but unknown/soft-deleted contract_number
  - 302 redirect to a presigned S3 URL when S3 + pdf_s3_key are available
  - 200 streamed PDF fallback when S3 is not configured
"""
from decimal import Decimal
from unittest.mock import patch
from uuid import uuid4

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

BASE = "/api/public/contracts"


@pytest_asyncio.fixture
async def pdf_tenant_account(db_session: AsyncSession):
    from src.apps.tenants.models.account import Account

    account = Account(
        organization_name="PDF Test Cemetery",
        subdomain=f"pdf-test-{uuid4().hex[:8]}",
        contact_email=f"admin-{uuid4().hex[:6]}@pdftest.com",
        plan="starter",
        status="active",
    )
    db_session.add(account)
    await db_session.flush()
    return account


async def _make_contract(db_session, account, *, contract_number, pdf_s3_key=None, deleted=False, status="signed"):
    from datetime import datetime, timezone
    from src.apps.sales.models.contract import Contract

    contract = Contract(
        tenant_id=account.id,
        contract_number=contract_number,
        status=status,
        total_amount=Decimal("1000.00"),
        purchaser_name="Jane Test",
        pdf_s3_key=pdf_s3_key,
    )
    if deleted:
        contract.deleted_at = datetime.now(timezone.utc)
    db_session.add(contract)
    await db_session.flush()
    return contract


@pytest.mark.asyncio
async def test_unknown_subdomain_returns_404(client: AsyncClient):
    resp = await client.get(f"{BASE}/does-not-exist-subdomain/CNT-0001/pdf")
    assert resp.status_code == 404


@pytest.mark.asyncio
async def test_unknown_contract_number_returns_404(client: AsyncClient, pdf_tenant_account):
    resp = await client.get(f"{BASE}/{pdf_tenant_account.subdomain}/NO-SUCH-CONTRACT/pdf")
    assert resp.status_code == 404


@pytest.mark.asyncio
async def test_soft_deleted_contract_returns_404(
    client: AsyncClient, db_session: AsyncSession, pdf_tenant_account
):
    await _make_contract(
        db_session, pdf_tenant_account, contract_number="CNT-DELETED", deleted=True
    )
    resp = await client.get(f"{BASE}/{pdf_tenant_account.subdomain}/CNT-DELETED/pdf")
    assert resp.status_code == 404


@pytest.mark.asyncio
async def test_draft_contract_returns_404_not_the_pdf(
    client: AsyncClient, db_session: AsyncSession, pdf_tenant_account
):
    """This endpoint is public and unauthenticated, and contract numbers are
    sequential/guessable — it must not disclose a PDF before the contract is
    signed, even though the authenticated GET /sales/contracts/{id}/pdf has
    no such gate (it doesn't need one; it's already access-controlled)."""
    await _make_contract(
        db_session, pdf_tenant_account, contract_number="CNT-DRAFT", status="draft"
    )
    resp = await client.get(f"{BASE}/{pdf_tenant_account.subdomain}/CNT-DRAFT/pdf")
    assert resp.status_code == 404


@pytest.mark.asyncio
async def test_signed_contract_with_s3_key_redirects_to_presigned_url(
    client: AsyncClient, db_session: AsyncSession, pdf_tenant_account
):
    await _make_contract(
        db_session,
        pdf_tenant_account,
        contract_number="CNT-S3",
        pdf_s3_key="contracts/cnt-s3.pdf",
    )

    fake_presigned = "https://s3.example.com/contracts/cnt-s3.pdf?X-Amz-Signature=abc"

    # `get_presigned_contract_pdf_url` does `from src.core.config import settings`
    # inside the function body (matching this codebase's lazy-import convention
    # for settings in service functions), so the real settings singleton's
    # attributes must be patched directly rather than the module reference.
    from src.core.config import settings as real_settings

    with patch.object(real_settings, "AWS_ACCESS_KEY_ID", "fake-key"), \
         patch.object(real_settings, "AWS_SECRET_ACCESS_KEY", "fake-secret"), \
         patch.object(real_settings, "AWS_REGION", "us-east-1"), \
         patch.object(real_settings, "S3_BUCKET", "fake-bucket"), \
         patch("boto3.client") as mock_boto_client:
        mock_s3 = mock_boto_client.return_value
        mock_s3.generate_presigned_url.return_value = fake_presigned

        resp = await client.get(
            f"{BASE}/{pdf_tenant_account.subdomain}/CNT-S3/pdf",
            follow_redirects=False,
        )

    assert resp.status_code == 302
    assert resp.headers["location"] == fake_presigned


@pytest.mark.asyncio
async def test_signed_contract_without_s3_streams_pdf_fallback(
    client: AsyncClient, db_session: AsyncSession, pdf_tenant_account
):
    """No pdf_s3_key at all — falls through to the on-demand ReportLab build."""
    await _make_contract(db_session, pdf_tenant_account, contract_number="CNT-STREAM")

    resp = await client.get(f"{BASE}/{pdf_tenant_account.subdomain}/CNT-STREAM/pdf")

    assert resp.status_code == 200
    assert resp.headers["content-type"] == "application/pdf"
    assert resp.content[:4] == b"%PDF"
