"""
Tests for POST /api/v1/sales/inquiries/log-call (INDL-25).
Covers T-06 through T-09 and related scenarios from the PRD.
"""
import pytest
from datetime import datetime, timezone, timedelta
from httpx import AsyncClient
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from src.apps.sales.models.contact_inquiry import ContactInquiry


LOG_CALL_URL = "/api/v1/sales/inquiries/log-call"

VALID_CALL_PAYLOAD = {
    "sender_name": "James Okonkwo",
    "sender_phone": "(613) 555-0311",
    "call_date": (datetime.now(timezone.utc) - timedelta(minutes=10)).isoformat(),
    "agent_name": "Asif Raza",
    "notes": "Interested in pre-need services for family of four.",
}


def _auth_headers(token: str):
    return {"Authorization": f"Bearer {token}"}


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


# ── T-06: Happy path — staff user ────────────────────────────────────────────

@pytest.mark.asyncio
async def test_log_call_happy_path(
    client: AsyncClient,
    db_session: AsyncSession,
    admin_token: str,
    test_account,
):
    """T-06: POST with valid fields by staff → 201; row with source_type='call_booking'."""
    response = await client.post(
        LOG_CALL_URL,
        json=VALID_CALL_PAYLOAD,
        headers=_tenant_headers(admin_token, str(test_account.id)),
    )
    assert response.status_code == 201
    body = response.json()
    assert body["success"] is True
    data = body["data"]
    assert data["source_type"] == "call_booking"
    assert data["sender_name"] == "James Okonkwo"
    assert data["agent_name"] == "Asif Raza"
    assert data["status"] == "new"
    assert data["reference_id"].startswith("INQ-")

    result = await db_session.execute(
        select(ContactInquiry).where(ContactInquiry.id == data["id"])
    )
    inq = result.scalar_one_or_none()
    assert inq is not None
    assert inq.source_type == "call_booking"
    assert inq.call_date is not None
    assert inq.agent_name == "Asif Raza"


# ── T-07: view_only user — 403 ───────────────────────────────────────────────

@pytest.mark.asyncio
async def test_log_call_view_only_forbidden(
    client: AsyncClient, db_session: AsyncSession, test_account
):
    """T-07: log-call by view_only user → 403."""
    from src.apps.auth.models.user import User
    from src.core.security import hash_password, create_access_token, build_token_payload

    view_user = User(
        tenant_id=test_account.id,
        email="view@testcemetery.com",
        password_hash=hash_password("Test1234"),
        first_name="View",
        last_name="Only",
        role="view_only",
        status="active",
    )
    db_session.add(view_user)
    await db_session.flush()

    token = create_access_token(build_token_payload(view_user, test_account))
    response = await client.post(
        LOG_CALL_URL,
        json=VALID_CALL_PAYLOAD,
        headers=_tenant_headers(token, str(test_account.id)),
    )
    assert response.status_code == 403


# ── T-08: call_date more than 5 minutes in the future — 422 ──────────────────

@pytest.mark.asyncio
async def test_log_call_future_date_rejected(
    client: AsyncClient, admin_token: str, test_account
):
    """T-08: call_date 10 minutes in the future → 422."""
    future_date = (datetime.now(timezone.utc) + timedelta(minutes=10)).isoformat()
    payload = {**VALID_CALL_PAYLOAD, "call_date": future_date}
    response = await client.post(
        LOG_CALL_URL,
        json=payload,
        headers=_tenant_headers(admin_token, str(test_account.id)),
    )
    assert response.status_code == 422


# ── T-09: Missing sender_phone — 422 ─────────────────────────────────────────

@pytest.mark.asyncio
async def test_log_call_missing_phone(
    client: AsyncClient, admin_token: str, test_account
):
    """T-09: log-call without sender_phone → 422."""
    payload = {k: v for k, v in VALID_CALL_PAYLOAD.items() if k != "sender_phone"}
    response = await client.post(
        LOG_CALL_URL,
        json=payload,
        headers=_tenant_headers(admin_token, str(test_account.id)),
    )
    assert response.status_code == 422


# ── T-11 (from log-email): Unauthenticated → 401 ─────────────────────────────

@pytest.mark.asyncio
async def test_log_call_unauthenticated(client: AsyncClient, test_account):
    """Unauthenticated request to log-call → 401."""
    response = await client.post(
        LOG_CALL_URL,
        json=VALID_CALL_PAYLOAD,
        headers={"X-Tenant-ID": str(test_account.id)},
    )
    assert response.status_code == 401


# ── Additional: call within 5-minute window is accepted ──────────────────────

@pytest.mark.asyncio
async def test_log_call_slightly_future_accepted(
    client: AsyncClient, admin_token: str, test_account
):
    """call_date 3 minutes in the future (within tolerance) → 201."""
    near_future = (datetime.now(timezone.utc) + timedelta(minutes=3)).isoformat()
    payload = {**VALID_CALL_PAYLOAD, "call_date": near_future}
    response = await client.post(
        LOG_CALL_URL,
        json=payload,
        headers=_tenant_headers(admin_token, str(test_account.id)),
    )
    assert response.status_code == 201


# ── T-13/14: GET /inquiries shows call_booking entries ───────────────────────

@pytest.mark.asyncio
async def test_list_inquiries_includes_call_log(
    client: AsyncClient,
    db_session: AsyncSession,
    admin_token: str,
    test_account,
):
    """T-13/14: After logging a call, GET /inquiries returns it with correct source_type."""
    await client.post(
        LOG_CALL_URL,
        json=VALID_CALL_PAYLOAD,
        headers=_tenant_headers(admin_token, str(test_account.id)),
    )
    list_resp = await client.get(
        "/api/v1/sales/inquiries",
        params={"status": "new"},
        headers=_tenant_headers(admin_token, str(test_account.id)),
    )
    assert list_resp.status_code == 200
    items = list_resp.json()["data"]
    call_items = [i for i in items if i["source_type"] == "call_booking"]
    assert len(call_items) >= 1


# ── T-16: PATCH /{id} updates agent_name ─────────────────────────────────────

@pytest.mark.asyncio
async def test_update_call_log_agent_name(
    client: AsyncClient,
    db_session: AsyncSession,
    admin_token: str,
    test_account,
):
    """T-16: PATCH inquiry to update agent_name → row updated."""
    create_resp = await client.post(
        LOG_CALL_URL,
        json=VALID_CALL_PAYLOAD,
        headers=_tenant_headers(admin_token, str(test_account.id)),
    )
    inq_id = create_resp.json()["data"]["id"]

    patch_resp = await client.patch(
        f"/api/v1/sales/inquiries/{inq_id}",
        json={"agent_name": "Updated Agent"},
        headers=_tenant_headers(admin_token, str(test_account.id)),
    )
    assert patch_resp.status_code == 200
    assert patch_resp.json()["data"]["agent_name"] == "Updated Agent"


# ── T-15: Convert call_booking to opportunity ─────────────────────────────────

@pytest.mark.asyncio
async def test_convert_call_booking_to_opportunity(
    client: AsyncClient,
    db_session: AsyncSession,
    admin_token: str,
    test_account,
):
    """T-15: POST /{id}/convert on call_booking → opportunity created; inquiry status=converted."""
    create_resp = await client.post(
        LOG_CALL_URL,
        json=VALID_CALL_PAYLOAD,
        headers=_tenant_headers(admin_token, str(test_account.id)),
    )
    inq_id = create_resp.json()["data"]["id"]

    convert_resp = await client.post(
        f"/api/v1/sales/inquiries/{inq_id}/convert",
        headers=_tenant_headers(admin_token, str(test_account.id)),
    )
    assert convert_resp.status_code == 201
    data = convert_resp.json()["data"]
    assert data["inquiry"]["status"] == "converted"
    assert data["opportunity_id"] is not None
