"""
Records -> Plot assignment must only ever point at a plot that actually
exists for the record's own tenant. Pydantic only checks plot_id is a
well-formed UUID, so without a service-level check a nonexistent id 500s
on the FK constraint and a wrong-tenant id is silently accepted.
"""
import uuid

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

from src.apps.auth.models.user import User
from src.apps.plots.models.plot import Plot
from src.apps.records.models.record import Record
from src.apps.tenants.models.account import Account
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, subdomain: str) -> Account:
    account = Account(
        organization_name=f"Cemetery {subdomain}",
        subdomain=subdomain,
        contact_email=f"admin@{subdomain}.com",
        plan="starter",
        status="active",
    )
    db.add(account)
    await db.flush()
    return account


async def _make_user(db: AsyncSession, account: Account) -> User:
    user = User(
        tenant_id=account.id,
        email=f"admin-{uuid.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))
    return {"Authorization": f"Bearer {token}", "X-Tenant-ID": str(account.id)}


async def _make_plot(db: AsyncSession, account: Account, plot_ref: str = "A-1") -> Plot:
    plot = Plot(tenant_id=account.id, plot_ref=plot_ref)
    db.add(plot)
    await db.flush()
    return plot


@pytest.mark.asyncio
async def test_create_record_with_valid_plot_succeeds(client, db_session: AsyncSession):
    account = await _make_account(db_session, f"acct-{uuid.uuid4().hex[:8]}")
    user = await _make_user(db_session, account)
    plot = await _make_plot(db_session, account)
    await db_session.flush()

    resp = await client.post(
        RECORDS_URL,
        json={"first_name": "Mary", "last_name": "Smith", "plot_id": str(plot.id)},
        headers=_auth_headers(user, account),
    )
    assert resp.status_code == 201, resp.text
    assert resp.json()["data"]["plot_id"] == str(plot.id)

    await db_session.refresh(plot)
    assert plot.status == "occupied"


@pytest.mark.asyncio
async def test_create_record_with_nonexistent_plot_returns_422_not_500(client, db_session: AsyncSession):
    account = await _make_account(db_session, f"acct-{uuid.uuid4().hex[:8]}")
    user = await _make_user(db_session, account)
    await db_session.flush()

    resp = await client.post(
        RECORDS_URL,
        json={"first_name": "Mary", "last_name": "Smith", "plot_id": str(uuid.uuid4())},
        headers=_auth_headers(user, account),
    )
    assert resp.status_code == 422, resp.text


@pytest.mark.asyncio
async def test_create_record_with_other_tenants_plot_rejected(client, db_session: AsyncSession):
    account_a = await _make_account(db_session, f"acct-a-{uuid.uuid4().hex[:8]}")
    account_b = await _make_account(db_session, f"acct-b-{uuid.uuid4().hex[:8]}")
    user_a = await _make_user(db_session, account_a)
    plot_b = await _make_plot(db_session, account_b)
    await db_session.flush()

    resp = await client.post(
        RECORDS_URL,
        json={"first_name": "Mary", "last_name": "Smith", "plot_id": str(plot_b.id)},
        headers=_auth_headers(user_a, account_a),
    )
    assert resp.status_code == 422, resp.text

    # Confirm the wrong-tenant plot's status was never touched either.
    await db_session.refresh(plot_b)
    assert plot_b.status == "vacant"


@pytest.mark.asyncio
async def test_update_record_with_other_tenants_plot_rejected(client, db_session: AsyncSession):
    account_a = await _make_account(db_session, f"acct-a-{uuid.uuid4().hex[:8]}")
    account_b = await _make_account(db_session, f"acct-b-{uuid.uuid4().hex[:8]}")
    user_a = await _make_user(db_session, account_a)
    plot_b = await _make_plot(db_session, account_b)
    record = Record(tenant_id=account_a.id, first_name="Mary", last_name="Smith")
    db_session.add(record)
    await db_session.flush()

    resp = await client.put(
        f"{RECORDS_URL}/{record.id}",
        json={"plot_id": str(plot_b.id)},
        headers=_auth_headers(user_a, account_a),
    )
    assert resp.status_code == 422, resp.text

    await db_session.refresh(record)
    assert record.plot_id is None
