"""Tests for Phase 21d — dashboard upgrade page."""

from __future__ import annotations

from unittest.mock import AsyncMock, MagicMock, patch

import httpx
import pytest

# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------

_TIER_IDS = {
    "team": "pri_team_annual",
    "agency": "pri_agency_annual",
    "enterprise": "pri_enterprise_annual",
}


def _make_customer_context(
    tier: str = "pro",
    customer_id: str = "cust_123",
    email: str = "user@example.com",
) -> MagicMock:
    ctx = MagicMock()
    ctx.tier = tier
    ctx.customer_id = customer_id
    ctx.email = email
    ctx.role = "owner"
    ctx.member_email = email
    ctx.features = ["hosted_dashboard"]
    ctx.consent_accepted = True
    ctx.tos_version = "1.0"
    ctx.privacy_version = "1.0"
    ctx.cancellation_pending = False
    ctx.subscription_expires_at = ""
    ctx.project_limit = 1
    ctx.batch_limit = 10
    return ctx


def _make_request(
    form_data: dict[str, str] | None = None,
) -> MagicMock:
    request = MagicMock()
    request.cookies = {"cf_session": "enc"}
    request.headers = {"X-CSRF-Token": "tok"}
    request.client = MagicMock()
    request.client.host = "127.0.0.1"
    request.state = MagicMock()
    request.form = AsyncMock(return_value=form_data or {})
    return request


_P_CSRF = "backend.dashboard.routes._validate_csrf"
_P_GET_CUST = "backend.shared.firestore.get_customer"
_P_PADDLE_KEY = "backend.ops.founder_metrics._get_paddle_api_key"
_P_AUDIT = "backend.dashboard.routes._audit"
_P_TIERS = "backend.dashboard.routes.TIER_PRICE_IDS"
_P_HTTPX = "backend.dashboard.routes.httpx.AsyncClient"


# ---------------------------------------------------------------------------
# Upgrade page rendering
# ---------------------------------------------------------------------------


@pytest.mark.asyncio
async def test_upgrade_page_pro() -> None:
    """Pro user sees Team, Agency, Enterprise options."""
    from backend.dashboard.routes import upgrade_page

    ctx = _make_customer_context(tier="pro")
    request = _make_request()
    response = await upgrade_page(request, ctx)
    assert response.status_code == 200
    body = response.body.decode()
    assert "team" in body.lower()
    assert "agency" in body.lower()
    assert "enterprise" in body.lower()


@pytest.mark.asyncio
async def test_upgrade_page_agency() -> None:
    """Agency user sees only Enterprise option."""
    from backend.dashboard.routes import upgrade_page

    ctx = _make_customer_context(tier="agency")
    request = _make_request()
    response = await upgrade_page(request, ctx)
    assert response.status_code == 200
    body = response.body.decode()
    assert "enterprise" in body.lower()
    assert "select pro" not in body.lower()
    assert "select team" not in body.lower()


@pytest.mark.asyncio
async def test_upgrade_page_enterprise() -> None:
    """Enterprise user sees no upgrade options."""
    from backend.dashboard.routes import upgrade_page

    ctx = _make_customer_context(tier="enterprise")
    request = _make_request()
    response = await upgrade_page(request, ctx)
    assert response.status_code == 200
    body = response.body.decode()
    # No "Select <tier>" buttons rendered
    assert "Select Pro" not in body
    assert "Select Team" not in body
    assert "Select Agency" not in body
    assert "Select Enterprise" not in body


@pytest.mark.asyncio
async def test_upgrade_community_not_shown() -> None:
    """Community has no subscription — redirects to pricing."""
    from backend.dashboard.routes import upgrade_page

    ctx = _make_customer_context(tier="community")
    request = _make_request()
    response = await upgrade_page(request, ctx)
    assert response.status_code == 302


# ---------------------------------------------------------------------------
# Upgrade preview
# ---------------------------------------------------------------------------


def _mock_paddle_client(
    response_data: dict | None = None,
    *,
    raise_error: bool = False,
) -> AsyncMock:
    """Build a mock httpx.AsyncClient for Paddle API."""
    mock_resp = MagicMock(spec=httpx.Response)
    if raise_error:
        mock_resp.text = "payment_failed"
        mock_resp.status_code = 402
        mock_resp.raise_for_status.side_effect = httpx.HTTPStatusError(
            "Payment failed",
            request=MagicMock(),
            response=mock_resp,
        )
    else:
        mock_resp.json.return_value = response_data or {}
        mock_resp.raise_for_status = MagicMock()

    client = AsyncMock()
    client.patch = AsyncMock(return_value=mock_resp)
    client.__aenter__ = AsyncMock(return_value=client)
    client.__aexit__ = AsyncMock(return_value=False)
    return client


@pytest.mark.asyncio
async def test_upgrade_preview() -> None:
    """Mock Paddle API, returns proration details."""
    from backend.dashboard.routes import upgrade_preview

    ctx = _make_customer_context(tier="pro")
    preview_data = {
        "data": {
            "immediate_transaction": {
                "details": {"totals": {"total": "150000"}},
            },
            "next_transaction": {
                "details": {"totals": {"total": "199900"}},
                "billing_period": {
                    "starts_at": "2027-03-15T00:00:00Z",
                },
            },
        }
    }
    client = _mock_paddle_client(preview_data)

    with (
        patch(_P_CSRF, new_callable=AsyncMock),
        patch(
            _P_GET_CUST,
            new_callable=AsyncMock,
            return_value={"paddle_subscription_id": "sub_123"},
        ),
        patch(
            _P_PADDLE_KEY,
            new_callable=AsyncMock,
            return_value="key",
        ),
        patch(_P_HTTPX, return_value=client),
        patch(_P_TIERS, _TIER_IDS),
    ):
        request = _make_request(form_data={"target_tier": "team"})
        response = await upgrade_preview(request, ctx)
        assert response.status_code == 200
        body = response.body.decode()
        assert "1,500.00" in body
        assert "Confirm Upgrade" in body


# ---------------------------------------------------------------------------
# Upgrade confirm
# ---------------------------------------------------------------------------


@pytest.mark.asyncio
async def test_upgrade_confirm_success() -> None:
    """Mock Paddle API, redirects to settings."""
    from backend.dashboard.routes import upgrade_confirm

    ctx = _make_customer_context(tier="pro")
    client = _mock_paddle_client({"data": {}})

    with (
        patch(_P_CSRF, new_callable=AsyncMock),
        patch(
            _P_GET_CUST,
            new_callable=AsyncMock,
            return_value={"paddle_subscription_id": "sub_123"},
        ),
        patch(
            _P_PADDLE_KEY,
            new_callable=AsyncMock,
            return_value="key",
        ),
        patch(_P_HTTPX, return_value=client),
        patch(_P_TIERS, _TIER_IDS),
        patch(_P_AUDIT, new_callable=AsyncMock),
    ):
        request = _make_request(form_data={"target_tier": "team"})
        response = await upgrade_confirm(request, ctx)
        assert response.status_code == 303
        assert response.headers.get("location") == "/dashboard/settings"


@pytest.mark.asyncio
async def test_upgrade_confirm_payment_failure() -> None:
    """Returns error card on payment failure."""
    from backend.dashboard.routes import upgrade_confirm

    ctx = _make_customer_context(tier="pro")
    client = _mock_paddle_client(raise_error=True)

    with (
        patch(_P_CSRF, new_callable=AsyncMock),
        patch(
            _P_GET_CUST,
            new_callable=AsyncMock,
            return_value={"paddle_subscription_id": "sub_123"},
        ),
        patch(
            _P_PADDLE_KEY,
            new_callable=AsyncMock,
            return_value="key",
        ),
        patch(_P_HTTPX, return_value=client),
        patch(_P_TIERS, _TIER_IDS),
    ):
        request = _make_request(form_data={"target_tier": "team"})
        response = await upgrade_confirm(request, ctx)
        assert response.status_code == 200
        body = response.body.decode()
        assert "Payment failed" in body


@pytest.mark.asyncio
async def test_upgrade_reject_downgrade() -> None:
    """Team → Pro → 400."""
    from backend.dashboard.routes import upgrade_confirm
    from fastapi import HTTPException

    ctx = _make_customer_context(tier="team")

    with patch(_P_CSRF, new_callable=AsyncMock):
        request = _make_request(form_data={"target_tier": "pro"})
        with pytest.raises(HTTPException) as exc_info:
            await upgrade_confirm(request, ctx)
        assert exc_info.value.status_code == 400
        assert "Downgrade" in str(exc_info.value.detail)
