"""Marking an opportunity lost must free its linked plot back to Available
("vacant") — the reverse of the cemetery-map -> sales sync (an on-hold or
reserved plot surfaces its linked opportunity's card as eligible for "Mark as
lost"). Covers both plot statuses a lost-eligible opportunity can realistically
carry (hold, reserved); an occupied plot is never reachable here because every
stage that can still be marked lost precedes the point a plot becomes occupied.
"""
import uuid

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

from src.apps.plots.models.plot import Plot
from src.apps.records.models.burial_info import BurialInfo
from src.apps.records.models.record import Record

pytestmark = pytest.mark.asyncio

OPPS_URL = "/api/v1/sales/opportunities"
PLOTS_URL = "/api/v1/plots"


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


async def _make_plot(db: AsyncSession, account, status: str) -> Plot:
    plot = Plot(tenant_id=account.id, plot_ref=f"P-{uuid.uuid4().hex[:6]}", status=status)
    db.add(plot)
    await db.flush()
    return plot


async def _plot_status(db: AsyncSession, plot_id) -> str:
    result = await db.execute(select(Plot).where(Plot.id == plot_id))
    return result.scalar_one().status


async def _create_opportunity_for_plot(client, token, tenant_id, plot_id) -> str:
    resp = await client.post(
        OPPS_URL,
        json={
            "family_name": "Hold Family",
            "care_type": "pre_need",
            "plot_id": str(plot_id),
            "estimated_value": 3000,
        },
        headers=_headers(token, tenant_id),
    )
    assert resp.status_code == 201, resp.text
    return resp.json()["data"]["id"]


@pytest.mark.parametrize("plot_status", ["hold", "reserved"])
async def test_mark_lost_releases_hold_or_reserved_plot_to_vacant(
    client, db_session: AsyncSession, admin_token: str, test_account, plot_status: str,
):
    plot = await _make_plot(db_session, test_account, status=plot_status)
    opp_id = await _create_opportunity_for_plot(client, admin_token, test_account.id, plot.id)

    resp = await client.post(
        f"{OPPS_URL}/{opp_id}/mark-lost",
        json={"loss_reason": "price"},
        headers=_headers(admin_token, test_account.id),
    )
    assert resp.status_code == 200, resp.text

    assert await _plot_status(db_session, plot.id) == "vacant"


async def test_mark_lost_releases_plot_and_deletes_its_pre_need_record(
    client, db_session: AsyncSession, admin_token: str, test_account,
):
    """A record placed on the plot while it was on hold for this deal is no
    longer valid once the deal falls through — same cascade as deliberately
    reverting the plot to Available, with no extra confirmation required
    since staff already confirmed via the Mark as Lost dialog."""
    plot = await _make_plot(db_session, test_account, status="hold")
    record = Record(
        tenant_id=test_account.id, plot_id=plot.id, first_name="Jane", last_name="Doe",
        status="active",
    )
    db_session.add(record)
    await db_session.flush()
    db_session.add(BurialInfo(
        record_id=record.id, tenant_id=test_account.id,
        interment_type="pre_need_no_service_yet",
    ))
    await db_session.flush()

    opp_id = await _create_opportunity_for_plot(client, admin_token, test_account.id, plot.id)

    resp = await client.post(
        f"{OPPS_URL}/{opp_id}/mark-lost",
        json={"loss_reason": "timing"},
        headers=_headers(admin_token, test_account.id),
    )
    assert resp.status_code == 200, resp.text

    assert await _plot_status(db_session, plot.id) == "vacant"
    await db_session.refresh(record)
    assert record.deleted_at is not None


async def test_mark_lost_does_not_touch_a_plot_that_is_still_vacant(
    client, db_session: AsyncSession, admin_token: str, test_account,
):
    plot = await _make_plot(db_session, test_account, status="vacant")
    opp_id = await _create_opportunity_for_plot(client, admin_token, test_account.id, plot.id)

    resp = await client.post(
        f"{OPPS_URL}/{opp_id}/mark-lost",
        json={"loss_reason": "other"},
        headers=_headers(admin_token, test_account.id),
    )
    assert resp.status_code == 200, resp.text
    assert await _plot_status(db_session, plot.id) == "vacant"
