"""Branch-scoped management reports with explicit accounting period semantics."""

import csv
from datetime import UTC, date, datetime
from io import StringIO
from typing import Literal
from uuid import UUID

from pydantic import BaseModel
from sqlalchemy.ext.asyncio import AsyncSession

from app.models.user import User
from app.schemas.dashboard import DashboardOverviewResponse, TopItemMetric
from app.services.dashboard_service import DashboardService
from app.services.report_pdf_renderer import render_management_pdf

ReportSection = Literal["combined", "financial", "operational"]


class ManagementReport(BaseModel):
    generated_at: datetime
    overview: DashboardOverviewResponse
    tests: list[TopItemMetric]
    packages: list[TopItemMetric]
    accounting_note: str = (
        "Invoiced and outstanding amounts belong to invoices issued in the selected period; "
        "collections by tender and daily collections use payment dates. Outstanding balances "
        "are current, not historical closing balances. Test volumes count directly ordered panels; "
        "package purchases are listed separately. Backlogs and total registered patients are current."
    )


class ManagementReportingService:
    def __init__(self) -> None:
        self.analytics = DashboardService()

    async def generate(
        self, db: AsyncSession, user: User, start: date, end: date, lab_id: UUID | None = None
    ) -> ManagementReport:
        # Fresh data for accounting exports; reuse tested tenant and date validation.
        overview = await self.analytics.get_overview(db, user, start, end, lab_id, refresh=True)
        repo = self.analytics.dashboard_repo
        tests = await repo.get_top_tests(db, overview.lab_id, start, end, limit=None)
        packages = await repo.get_top_packages(db, overview.lab_id, start, end, limit=None)
        return ManagementReport(
            generated_at=datetime.now(UTC), overview=overview, tests=tests, packages=packages
        )

    def rows(self, report: ManagementReport, section: ReportSection) -> list[list[str]]:
        overview = report.overview
        rows = [["Section", "Metric / date", "Value", "Orders / count", "Patients", "Invoiced (NPR)"]]
        rows += [
            ["Period", "Start (AD)", str(overview.period_start), "", "", ""],
            ["Period", "End (AD, inclusive)", str(overview.period_end), "", "", ""],
            ["Scope", "Branch", str(overview.lab_id or "All branches"), "", "", ""],
            ["Generated", "UTC", report.generated_at.isoformat(), "", "", ""],
            ["Notes", "Accounting basis", report.accounting_note, "", "", ""],
        ]
        if section != "operational":
            for key, value in overview.financial.model_dump().items():
                if key == "revenue_by_tender":
                    for tender, amount in value.items():
                        rows.append(["Collections by tender (NPR)", tender, str(amount), "", "", ""])
                elif key == "total_collected":
                    rows.append(
                        [
                            "Financial (NPR)",
                            "Payments against period invoices (current)",
                            str(value),
                            "",
                            "",
                            "",
                        ]
                    )
                else:
                    rows.append(["Financial (NPR)", key.replace("_", " "), str(value), "", "", ""])
        if section != "financial":
            for key, value in overview.operational.model_dump().items():
                if isinstance(value, dict):
                    for status, count in value.items():
                        rows.append([key.replace("_", " "), status, str(count), "", "", ""])
                else:
                    rows.append(["Operational", key.replace("_", " "), str(value), "", "", ""])
            for category, items in (("Direct test panels", report.tests), ("Packages", report.packages)):
                for item in items:
                    rows.append(
                        [
                            category,
                            f"{item.code} · {item.name}",
                            "",
                            str(item.count),
                            "",
                            str(item.total_value),
                        ]
                    )
        for point in overview.trend:
            rows.append(
                [
                    "Daily",
                    point.date,
                    str(point.revenue) if section != "operational" else "",
                    str(point.order_count) if section != "financial" else "",
                    str(point.patient_count) if section != "financial" else "",
                    str(point.invoiced_amount) if section != "operational" else "",
                ]
            )
        return rows

    def csv_bytes(self, report: ManagementReport, section: ReportSection) -> bytes:
        out = StringIO(newline="")
        writer = csv.writer(out)
        # Prevent spreadsheet formula execution from catalog/user supplied text.
        for row in self.rows(report, section):
            writer.writerow(
                ["'" + cell if cell.lstrip().startswith(("=", "+", "-", "@")) else cell for cell in row]
            )
        return out.getvalue().encode("utf-8-sig")

    def pdf_bytes(self, report: ManagementReport, section: ReportSection) -> bytes:
        return render_management_pdf(self.rows(report, section), section)
