"""Management reporting permission, tenant, export and range regressions."""

from datetime import UTC, datetime
from decimal import Decimal
from uuid import uuid4

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

from app.core.security import create_access_token, hash_password
from app.models.audit import AuditLog
from app.models.billing import Bill, BillStatus
from app.models.lab import Lab
from app.models.order import OrderStatus, VisitOrder
from app.models.patient import Gender, Patient
from app.models.user import Permission, User, UserRole
from app.repositories.permission_repository import PermissionRepository
from app.schemas.dashboard import DashboardOverviewResponse, FinancialStats, OperationalStats, TopItemMetric
from app.services.management_reporting_service import ManagementReport, ManagementReportingService


async def actor(db: AsyncSession, role: UserRole, with_lab: bool = True) -> tuple[User, dict[str, str]]:
    await PermissionRepository().seed_permissions(db)
    lab = Lab(name="Reporting branch", address="Kathmandu")
    db.add(lab)
    await db.flush()
    user = User(
        name="Reporting user",
        phone="9841111111",
        email="reporting@example.com",
        hashed_password=hash_password("Password123!"),
        role=role,
        lab_id=lab.id if with_lab else None,
    )
    db.add(user)
    await db.commit()
    return user, {"Authorization": f"Bearer {create_access_token(str(user.id))}"}


@pytest.mark.asyncio
async def test_report_range_export_and_audit(client: AsyncClient, db_session: AsyncSession) -> None:
    user, headers = await actor(db_session, UserRole.ACCOUNTANT)
    patient = Patient(
        name="Report patient",
        phone="9842222222",
        gender=Gender.MALE,
        patient_code="REP-1",
        lab_id=user.lab_id,
    )
    db_session.add(patient)
    await db_session.flush()
    order = VisitOrder(
        order_number="REP-ORDER-1", patient_id=patient.id, lab_id=user.lab_id, status=OrderStatus.PENDING
    )
    db_session.add(order)
    await db_session.flush()
    db_session.add(
        Bill(
            order_id=order.id,
            patient_id=patient.id,
            lab_id=user.lab_id,
            bill_number="REP-BILL-1",
            subtotal=100,
            total_amount=100,
            paid_amount=0,
            due_amount=100,
            status=BillStatus.UNPAID,
            created_at=datetime(2026, 10, 1, 6, tzinfo=UTC),
        )
    )
    await db_session.commit()
    params = {"start_date": "2026-10-01", "end_date": "2026-10-31"}
    result = await client.get("/api/v1/management-reporting", params=params, headers=headers)
    assert result.status_code == 200, result.text
    data = result.json()["data"]
    assert Decimal(data["overview"]["financial"]["total_invoiced"]) == 100
    assert data["overview"]["lab_id"] == str(user.lab_id)
    assert len(data["overview"]["trend"]) == 31
    for format in ("csv", "pdf"):
        exported = await client.get(
            "/api/v1/management-reporting/export", params={**params, "format": format}, headers=headers
        )
        assert exported.status_code == 200, exported.text
        assert exported.headers["cache-control"] == "no-store"
        assert "attachment" in exported.headers["content-disposition"]
        if format == "pdf":
            assert exported.content.startswith(b"%PDF")
        else:
            assert "total invoiced,100" in exported.text
    events = (
        (await db_session.execute(select(AuditLog).where(AuditLog.action == "MANAGEMENT_REPORT_EXPORTED")))
        .scalars()
        .all()
    )
    assert len(events) == 2


@pytest.mark.asyncio
async def test_reporting_cannot_cross_branch_or_use_unbounded_dates(
    client: AsyncClient, db_session: AsyncSession
) -> None:
    _, headers = await actor(db_session, UserRole.ACCOUNTANT)
    for params, status in [
        ({"start_date": "2026-10-01", "end_date": "2026-10-31", "lab_id": str(uuid4())}, 403),
        ({"start_date": "2026-10-31", "end_date": "2026-10-01"}, 422),
        ({"start_date": "2020-01-01", "end_date": "2026-10-31"}, 422),
    ]:
        result = await client.get("/api/v1/management-reporting", params=params, headers=headers)
        assert result.status_code == status, result.text


@pytest.mark.asyncio
async def test_reporting_permission_can_be_disabled(client: AsyncClient, db_session: AsyncSession) -> None:
    _, headers = await actor(db_session, UserRole.ACCOUNTANT)
    permission = (
        await db_session.execute(select(Permission).where(Permission.code == "reporting:export"))
    ).scalar_one()
    permission.is_active = False
    await db_session.commit()
    params = {"start_date": "2026-10-01", "end_date": "2026-10-31"}
    assert (
        await client.get("/api/v1/management-reporting/export", params=params, headers=headers)
    ).status_code == 403
    assert (
        await client.get("/api/v1/management-reporting", params=params, headers=headers)
    ).status_code == 200


@pytest.mark.asyncio
async def test_reception_cannot_access_management_financials(
    client: AsyncClient, db_session: AsyncSession
) -> None:
    _, headers = await actor(db_session, UserRole.RECEPTIONIST)
    result = await client.get(
        "/api/v1/management-reporting",
        params={"start_date": "2026-10-01", "end_date": "2026-10-31"},
        headers=headers,
    )
    assert result.status_code == 403


def test_csv_neutralizes_catalog_formulas() -> None:
    report = ManagementReport(
        generated_at=datetime.now(UTC),
        overview=DashboardOverviewResponse(
            period_start="2026-10-01",
            period_end="2026-10-31",
            operational=OperationalStats(),
            financial=FinancialStats(),
        ),
        tests=[TopItemMetric(id=uuid4(), code="=DANGEROUS", name="CSV formula", count=1)],
        packages=[],
    )
    content = ManagementReportingService().csv_bytes(report, "operational").decode("utf-8-sig")
    assert "'=DANGEROUS" in content
