import calendar
from collections import defaultdict
from datetime import date

from django.db.models import Count
from django.db.models.functions import TruncMonth

from apps.integrations.models import HISPatientVisit

PATIENT_TYPE_MAP = {
    "OP": "OPD",
    "ED": "ER",
    "IP": "IP",
}

AREA_ORDER = ["OPD", "ER", "IP"]


class CensusService:
    def __init__(self, hospital_id, year=None):
        self.hospital_id = hospital_id
        self.year = year or date.today().year

    def get_monthly_counts(self, year=None):
        year = year if year is not None else self.year
        qs = (
            HISPatientVisit.objects.filter(
                hospital_id=self.hospital_id,
                admit_date__year=year,
            )
            .annotate(month=TruncMonth("admit_date"))
            .values("month", "patient_type")
            .annotate(count=Count("id"))
            .order_by("month", "patient_type")
        )

        data = {}
        for row in qs:
            m = row["month"].month
            label = PATIENT_TYPE_MAP.get(row["patient_type"], row["patient_type"])
            if m not in data:
                data[m] = {"OPD": 0, "ER": 0, "IP": 0}
            data[m][label] = row["count"]
        return data

    def get_quarterly_data(self, year=None):
        year = year if year is not None else self.year
        monthly = self.get_monthly_counts(year)
        quarters = {}
        for q in range(1, 5):
            months_in_q = [(q - 1) * 3 + i for i in range(1, 4)]
            q_data = {"months": {}, "totals": {"OPD": 0, "ER": 0, "IP": 0}}
            for m in months_in_q:
                m_data = monthly.get(m, {"OPD": 0, "ER": 0, "IP": 0})
                q_data["months"][m] = m_data
                for area in AREA_ORDER:
                    q_data["totals"][area] += m_data[area]
            quarters[q] = q_data
        return quarters

    def get_year_totals(self, year=None):
        year = year if year is not None else self.year
        monthly = self.get_monthly_counts(year)
        totals = {"OPD": 0, "ER": 0, "IP": 0, "total": 0}
        for m_data in monthly.values():
            for area in AREA_ORDER:
                totals[area] += m_data[area]
        totals["total"] = sum(totals[a] for a in AREA_ORDER)
        return totals

    def get_comparison_data(self, years=None):
        if years is None:
            years = [self.year - 2, self.year - 1, self.year]
        comparison = {}
        for y in years:
            comparison[y] = {
                "quarterly": self.get_quarterly_data(y),
                "yearly": self.get_year_totals(y),
            }
        return comparison

    def get_available_years(self):
        years = (
            HISPatientVisit.objects.filter(hospital_id=self.hospital_id)
            .values_list("admit_date__year", flat=True)
            .distinct()
            .order_by("admit_date__year")
        )
        return list(years)

    def get_chart_data(self, years=None):
        if years is None:
            years = [self.year - 2, self.year - 1, self.year]
        years = sorted(years)

        yoy_data = {area: {y: [] for y in years} for area in AREA_ORDER}
        for y in years:
            q_data = self.get_quarterly_data(y)
            for q in range(1, 5):
                for area in AREA_ORDER:
                    yoy_data[area][y].append(q_data[q]["totals"][area])

        monthly_data = {area: [] for area in AREA_ORDER}
        monthly = self.get_monthly_counts(self.year)
        for m in range(1, 13):
            m_data = monthly.get(m, {"OPD": 0, "ER": 0, "IP": 0})
            for area in AREA_ORDER:
                monthly_data[area].append(m_data[area])

        return {
            "yoy": yoy_data,
            "monthly": monthly_data,
            "quarters": ["Q1", "Q2", "Q3", "Q4"],
            "months": [calendar.month_abbr[i] for i in range(1, 13)],
            "years": years,
        }
