"""
Free-trial / plan records-limit enforcement on POST /records.

While a tenant's Subscription.payment_status == "trialing" (14-day free
trial, card saved but not yet charged), every plan is capped at
TRIAL_RECORD_LIMIT regardless of the plan's real PLAN_LIMITS ceiling. The
moment payment succeeds (payment_status -> "paid"), the plan's real limit
applies again. TRIAL_RECORD_LIMIT is patched down to 2 in these tests so we
don't need to insert hundreds of rows to exercise the boundary.
"""
from unittest.mock import patch
from uuid import uuid4

import pytest
from sqlalchemy.ext.asyncio import AsyncSession

from src.apps.auth.models.user import User
from src.apps.records.models.record import Record
from src.apps.tenants.models.account import Account
from src.apps.tenants.models.subscription import Subscription
from src.core.security import build_token_payload, create_access_token, hash_password

pytestmark = pytest.mark.asyncio

RECORDS_URL = "/api/v1/records"


async def _make_account(db: AsyncSession, *, plan: str = "enterprise") -> Account:
    uid = uuid4().hex[:8]
    account = Account(
        organization_name=f"Trial Cemetery {uid}",
        subdomain=f"trial-{uid}",
        contact_email=f"admin-{uid}@trial.test",
        plan=plan,
        status="active",
    )
    db.add(account)
    await db.flush()
    return account


async def _make_subscription(db: AsyncSession, account: Account, *, payment_status: str) -> Subscription:
    sub = Subscription(
        account_id=account.id,
        plan=account.plan,
        status="trialing" if payment_status == "trialing" else "active",
        payment_status=payment_status,
    )
    db.add(sub)
    await db.flush()
    return sub


async def _make_user(db: AsyncSession, account: Account) -> User:
    user = User(
        tenant_id=account.id,
        email=f"admin-{uuid4().hex[:8]}@{account.subdomain}.com",
        password_hash=hash_password("TestPassword123"),
        first_name="Admin",
        last_name="User",
        role="administrator",
        status="active",
    )
    db.add(user)
    await db.flush()
    return user


def _auth_headers(user: User, account: Account) -> dict:
    token = create_access_token(build_token_payload(user, account))
    return {"Authorization": f"Bearer {token}", "X-Tenant-ID": str(account.id)}


async def _fill_records(db: AsyncSession, account: Account, count: int) -> None:
    for i in range(count):
        db.add(Record(tenant_id=account.id, first_name="Existing", last_name=f"Record{i}"))
    await db.flush()


@patch("src.core.features.TRIAL_RECORD_LIMIT", 2)
async def test_trial_account_blocked_at_trial_limit_even_on_enterprise_plan(client, db_session):
    """Enterprise normally has an unlimited (None) records limit — but while
    trialing, TRIAL_RECORD_LIMIT still applies."""
    account = await _make_account(db_session, plan="enterprise")
    await _make_subscription(db_session, account, payment_status="trialing")
    user = await _make_user(db_session, account)
    await _fill_records(db_session, account, 2)

    resp = await client.post(
        RECORDS_URL,
        json={"first_name": "One", "last_name": "TooMany"},
        headers=_auth_headers(user, account),
    )
    assert resp.status_code == 403, resp.text
    body = resp.json()
    assert body["error_code"] == "TRIAL_RECORD_LIMIT_REACHED"
    assert "trial" in body["message"].lower()


@patch("src.core.features.TRIAL_RECORD_LIMIT", 2)
async def test_trial_account_can_insert_up_to_the_trial_limit(client, db_session):
    account = await _make_account(db_session, plan="starter")
    await _make_subscription(db_session, account, payment_status="trialing")
    user = await _make_user(db_session, account)
    await _fill_records(db_session, account, 1)

    resp = await client.post(
        RECORDS_URL,
        json={"first_name": "Last", "last_name": "Slot"},
        headers=_auth_headers(user, account),
    )
    assert resp.status_code == 201, resp.text


@patch("src.core.features.TRIAL_RECORD_LIMIT", 2)
async def test_paid_account_is_not_capped_by_trial_limit(client, db_session):
    """Once payment_status flips to "paid", the trial cap no longer applies
    even though TRIAL_RECORD_LIMIT is patched to a tiny value here — the
    plan's real (much larger) limit governs instead."""
    account = await _make_account(db_session, plan="starter")
    await _make_subscription(db_session, account, payment_status="paid")
    user = await _make_user(db_session, account)
    await _fill_records(db_session, account, 2)

    resp = await client.post(
        RECORDS_URL,
        json={"first_name": "Paid", "last_name": "Tenant"},
        headers=_auth_headers(user, account),
    )
    assert resp.status_code == 201, resp.text


@patch("src.core.features.TRIAL_RECORD_LIMIT", 2)
async def test_account_with_no_subscription_row_is_not_trial_capped(client, db_session):
    """Admin-provisioned tenants with no Subscription row at all (payment_status
    concept doesn't apply) fall back to their plan's real limit, not the trial cap."""
    account = await _make_account(db_session, plan="starter")
    user = await _make_user(db_session, account)
    await _fill_records(db_session, account, 2)

    resp = await client.post(
        RECORDS_URL,
        json={"first_name": "NoSub", "last_name": "Tenant"},
        headers=_auth_headers(user, account),
    )
    assert resp.status_code == 201, resp.text


@patch("src.core.features.TRIAL_RECORD_LIMIT", 2)
async def test_trial_limit_error_does_not_create_a_record(client, db_session):
    """A blocked create must not leave a partial row behind."""
    account = await _make_account(db_session, plan="professional")
    await _make_subscription(db_session, account, payment_status="trialing")
    user = await _make_user(db_session, account)
    await _fill_records(db_session, account, 2)

    resp = await client.post(
        RECORDS_URL,
        json={"first_name": "Blocked", "last_name": "Insert"},
        headers=_auth_headers(user, account),
    )
    assert resp.status_code == 403

    from sqlalchemy import select, func
    count = (
        await db_session.execute(
            select(func.count()).select_from(Record).where(Record.tenant_id == account.id)
        )
    ).scalar_one()
    assert count == 2
