import logging
from collections import defaultdict

from django.db.models import Count, Avg, Sum

logger = logging.getLogger(__name__)

QUARTER_MONTHS = {
    1: [1, 2, 3],
    2: [4, 5, 6],
    3: [7, 8, 9],
    4: [10, 11, 12],
}


class BaseDataSource:
    name = ""
    label = ""
    description = ""
    available_keys = []

    def __init__(self, hospital_id, year, quarter=None):
        self.hospital_id = hospital_id
        self.year = year
        self.quarter = quarter

    def _active_months(self):
        if self.quarter:
            return QUARTER_MONTHS[self.quarter]
        return list(range(1, 13))

    def fetch(self):
        raise NotImplementedError

    def get_data_summary(self, data):
        raise NotImplementedError


class ComplaintsDataSource(BaseDataSource):
    name = "complaints"
    label = "Complaints Quarterly"
    description = "Patient complaints data aggregated quarterly from ComplaintQuarterlyService"
    available_keys = [
        "kpi_totals", "source_totals", "location_totals", "dept_totals",
        "escalated", "response", "monthly_totals", "monthly_resolution",
        "satisfaction_rate", "satisfaction_total", "months",
    ]

    def fetch(self):
        from apps.dashboard.services.complaint_quarterly_service import (
            ComplaintQuarterlyService,
            TIMELINE_BUCKETS,
        )
        from apps.complaints.models import Complaint

        svc = ComplaintQuarterlyService(hospital_id=self.hospital_id, year=self.year)
        months = self._active_months()

        kpi = svc.get_kpi_data()
        source = svc.get_source_breakdown()
        location = svc.get_location_breakdown()
        dept = svc.get_dept_type_breakdown()
        escalated = svc.get_escalated_breakdown()
        response = svc.get_response_rates()
        chart = svc.get_chart_data()

        filtered_kpi_months = {m: kpi["months"][m] for m in months}
        kpi_totals = {
            "total": sum(r["total"] for r in filtered_kpi_months.values()),
            "closed": sum(r["closed"] for r in filtered_kpi_months.values()),
            "resolved_72h": sum(r["resolved_72h"] for r in filtered_kpi_months.values()),
            "activated": sum(r["activated"] for r in filtered_kpi_months.values()),
        }
        t = kpi_totals
        kpi_totals["resolution_rate"] = round(t["closed"] / t["total"], 4) if t["total"] > 0 else 0
        kpi_totals["resolved_72h_rate"] = round(t["resolved_72h"] / t["total"], 4) if t["total"] > 0 else 0

        source_totals = {
            "internal": sum(source[m]["internal"] for m in months),
            "external": sum(source[m]["external"] for m in months),
            "moh": sum(source[m]["moh"] for m in months),
            "chi": sum(source[m]["chi"] for m in months),
            "insurance": sum(source[m]["insurance"] for m in months),
            "total": sum(source[m]["total"] for m in months),
        }

        location_totals = {
            "ip": sum(location[m]["ip"] for m in months),
            "op": sum(location[m]["op"] for m in months),
            "er": sum(location[m]["er"] for m in months),
        }

        dept_totals = {
            "medical": sum(dept[m]["medical"] for m in months),
            "admin": sum(dept[m]["admin"] for m in months),
            "nursing": sum(dept[m]["nursing"] for m in months),
            "support": sum(dept[m]["support"] for m in months),
        }

        monthly_totals = [filtered_kpi_months[m]["total"] for m in months]
        monthly_resolution = [filtered_kpi_months[m]["resolution_rate"] for m in months]

        satisfaction_qs = Complaint.objects.filter(
            hospital_id=self.hospital_id,
            created_at__year=self.year,
            created_at__month__in=months,
            satisfaction__isnull=False,
        ).exclude(satisfaction="")
        sat_total = satisfaction_qs.count()
        sat_satisfied = satisfaction_qs.filter(satisfaction="satisfied").count()
        satisfaction_rate = round(sat_satisfied / sat_total, 4) if sat_total > 0 else 0

        return {
            "kpi_totals": kpi_totals,
            "source_totals": source_totals,
            "location_totals": location_totals,
            "dept_totals": dept_totals,
            "escalated": escalated,
            "response": response,
            "monthly_totals": monthly_totals,
            "monthly_resolution": monthly_resolution,
            "satisfaction_rate": satisfaction_rate,
            "satisfaction_total": sat_total,
            "months": months,
        }

    def get_data_summary(self, data):
        kpi = data["kpi_totals"]
        st = data["source_totals"]
        dt = data["dept_totals"]
        esc = data["escalated"]
        return (
            f"Report Type: Patient Experience / Complaints\n"
            f"Total Complaints: {kpi['total']}\n"
            f"Resolution Rate: {kpi['resolution_rate']*100:.1f}%\n"
            f"72h Resolution Rate: {kpi['resolved_72h_rate']*100:.1f}%\n"
            f"Patient Satisfaction: {data['satisfaction_rate']*100:.1f}%\n"
            f"Sources: Internal {st['internal']}, MOH {st['moh']}, CCHI {st['chi']}\n"
            f"Depts: Medical {dt['medical']}, Admin {dt['admin']}, Nursing {dt['nursing']}\n"
            f"Escalated: {esc['total_escalated']} ({esc['escalation_rate']*100:.1f}%)\n"
        )


class PhysicianRatingDataSource(BaseDataSource):
    name = "physicians"
    label = "Physician Ratings"
    description = "Doctor ratings from PhysicianMonthlyRating aggregated data"
    available_keys = [
        "overall", "by_department", "consolidated", "monthly_avg", "months",
    ]

    def fetch(self):
        from apps.physicians.models import PhysicianMonthlyRating

        months = self._active_months()
        ratings_qs = PhysicianMonthlyRating.objects.filter(
            staff__hospital_id=self.hospital_id,
            year=self.year,
            month__in=months,
        ).select_related("staff", "staff__department")

        overall = ratings_qs.aggregate(
            total_doctors=Count("staff", distinct=True),
            total_surveys=Sum("total_surveys"),
            avg_rating=Avg("average_rating"),
            total_positive=Sum("positive_count"),
            total_neutral=Sum("neutral_count"),
            total_negative=Sum("negative_count"),
            total_r1=Sum("rating_1_count"),
            total_r2=Sum("rating_2_count"),
            total_r3=Sum("rating_3_count"),
            total_r4=Sum("rating_4_count"),
            total_r5=Sum("rating_5_count"),
        )

        total_surveys = overall["total_surveys"] or 0
        total_positive = overall["total_positive"] or 0
        total_negative = overall["total_negative"] or 0
        avg_rating = overall["avg_rating"] or 0

        doctor_ratings = defaultdict(list)
        for r in ratings_qs:
            dept_name = str(r.staff.department) if r.staff.department else "Uncategorized"
            doctor_ratings[r.staff_id].append({
                "name": r.staff.name or f"{r.staff.first_name} {r.staff.last_name}",
                "employee_id": r.staff.employee_id or "",
                "department": dept_name,
                "avg_rating": float(r.average_rating),
                "total_surveys": r.total_surveys,
                "positive": r.positive_count,
                "neutral": r.neutral_count,
                "negative": r.negative_count,
                "r1": r.rating_1_count,
                "r2": r.rating_2_count,
                "r3": r.rating_3_count,
                "r4": r.rating_4_count,
                "r5": r.rating_5_count,
            })

        consolidated = {}
        for sid, entries in doctor_ratings.items():
            total_s = sum(e["total_surveys"] for e in entries)
            if total_s == 0:
                continue
            weighted_sum = sum(e["avg_rating"] * e["total_surveys"] for e in entries)
            avg = round(weighted_sum / total_s, 2)
            consolidated[sid] = {
                "name": entries[0]["name"],
                "employee_id": entries[0]["employee_id"],
                "department": entries[0]["department"],
                "avg_rating": avg,
                "total_surveys": total_s,
                "r1": sum(e["r1"] for e in entries),
                "r2": sum(e["r2"] for e in entries),
                "r3": sum(e["r3"] for e in entries),
                "r4": sum(e["r4"] for e in entries),
                "r5": sum(e["r5"] for e in entries),
                "positive": sum(e["positive"] for e in entries),
                "neutral": sum(e["neutral"] for e in entries),
                "negative": sum(e["negative"] for e in entries),
            }

        by_department = defaultdict(list)
        for doc in consolidated.values():
            by_department[doc["department"]].append(doc)

        for dept_docs in by_department.values():
            dept_docs.sort(key=lambda d: d["avg_rating"], reverse=True)

        monthly_avg = {}
        for m in months:
            m_avg = ratings_qs.filter(month=m).aggregate(a=Avg("average_rating"))["a"]
            monthly_avg[m] = round(float(m_avg), 2) if m_avg else 0

        return {
            "overall": {
                "total_doctors": overall["total_doctors"] or 0,
                "total_surveys": total_surveys,
                "avg_rating": round(float(avg_rating), 2),
                "total_positive": total_positive,
                "total_negative": total_negative,
                "total_neutral": overall["total_neutral"] or 0,
                "r1": overall["total_r1"] or 0,
                "r2": overall["total_r2"] or 0,
                "r3": overall["total_r3"] or 0,
                "r4": overall["total_r4"] or 0,
                "r5": overall["total_r5"] or 0,
                "positive_pct": round(total_positive / total_surveys * 100, 1) if total_surveys else 0,
                "negative_pct": round(total_negative / total_surveys * 100, 1) if total_surveys else 0,
            },
            "by_department": dict(by_department),
            "consolidated": consolidated,
            "monthly_avg": monthly_avg,
            "months": months,
        }

    def get_data_summary(self, data):
        o = data["overall"]
        return (
            f"Report Type: Physician / Doctor Ratings\n"
            f"Total Physicians: {o['total_doctors']}\n"
            f"Total Surveys: {o['total_surveys']}\n"
            f"Average Rating: {o['avg_rating']}/5.0\n"
            f"Positive (>=4): {o['positive_pct']}%\n"
            f"Negative (<3): {o['negative_pct']}%\n"
            f"Departments: {len(data['by_department'])}\n"
        )


REPORT_DATA_SOURCES = {
    "complaints": {
        "label": "Complaints Quarterly",
        "description": "Patient complaints data with KPIs, sources, departments, and response rates",
        "source_class": ComplaintsDataSource,
        "data_keys": ComplaintsDataSource.available_keys,
    },
    "physicians": {
        "label": "Physician Ratings",
        "description": "Doctor patient ratings with department breakdowns and rating distributions",
        "source_class": PhysicianRatingDataSource,
        "data_keys": PhysicianRatingDataSource.available_keys,
    },
}


def get_data_source(source_key):
    entry = REPORT_DATA_SOURCES.get(source_key)
    if not entry:
        raise ValueError(f"Unknown data source: {source_key}. Available: {list(REPORT_DATA_SOURCES.keys())}")
    return entry["source_class"]


def get_data_source_choices():
    return [(key, entry["label"]) for key, entry in REPORT_DATA_SOURCES.items()]
