from collections import defaultdict
from datetime import timedelta

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

from apps.observations.models import Observation

SOURCE_MAP = {
    "staff_portal": "Portal",
    "web_form": "Portal",
    "mobile_app": "Barcode",
    "email": "Referral",
    "call_center": "In-person",
    "other": "Other",
}

STATUS_LABELS = {
    "new": "New",
    "triaged": "Triaged",
    "assigned": "Assigned",
    "in_progress": "In Progress",
    "resolved": "Resolved",
    "closed": "Closed",
    "rejected": "Rejected",
    "duplicate": "Duplicate",
    "contacted": "Contacted",
    "contacted_no_response": "No Response",
}

STATUS_COLORS = {
    "new": "#3B82F6",
    "triaged": "#06B6D4",
    "assigned": "#8B5CF6",
    "in_progress": "#F59E0B",
    "resolved": "#10B981",
    "closed": "#6B7280",
    "rejected": "#EF4444",
    "duplicate": "#9CA3AF",
    "contacted": "#22C55E",
    "contacted_no_response": "#DC2626",
}

SEVERITY_LABELS = {
    "low": "Low",
    "medium": "Medium",
    "high": "High",
    "critical": "Critical",
}

SEVERITY_COLORS = {
    "low": "#10B981",
    "medium": "#F59E0B",
    "high": "#F97316",
    "critical": "#EF4444",
}

SEVERITY_ORDER = ["low", "medium", "high", "critical"]


class ObservationReportService:
    def __init__(self, hospital_id, date_from, date_to):
        self.hospital_id = hospital_id
        self.date_from = date_from
        self.date_to = date_to

    def _base_qs(self):
        return Observation.objects.filter(
            hospital_id=self.hospital_id,
            created_at__date__gte=self.date_from,
            created_at__date__lte=self.date_to,
        ).select_related(
            "category", "assigned_department", "assigned_to", "hospital"
        )

    def get_summary(self):
        qs = self._base_qs()
        total = qs.count()

        by_status = dict(
            qs.values_list("status").annotate(count=Count("id")).order_by()
        )
        by_severity = dict(
            qs.values_list("severity").annotate(count=Count("id")).order_by()
        )

        resolved_statuses = ["resolved", "closed"]
        resolved_count = sum(by_status.get(s, 0) for s in resolved_statuses)
        resolution_rate = (resolved_count / total * 100) if total > 0 else 0

        by_source = dict(
            qs.values_list("source").annotate(count=Count("id")).order_by()
        )

        return {
            "total": total,
            "by_status": by_status,
            "by_severity": by_severity,
            "by_source": by_source,
            "resolution_rate": round(resolution_rate, 1),
            "resolved_count": resolved_count,
        }

    def get_daily_trend(self):
        qs = self._base_qs()
        daily_raw = (
            qs.annotate(day=TruncDate("created_at"))
            .values("day")
            .annotate(count=Count("id"))
            .order_by("day")
        )
        daily = {}
        for entry in daily_raw:
            if entry["day"]:
                daily[entry["day"]] = entry["count"]
        result = []
        current = self.date_from
        while current <= self.date_to:
            result.append({
                "day": current,
                "label": current.strftime("%b %d"),
                "count": daily.get(current, 0),
            })
            current += timedelta(days=1)
        return result

    def get_category_breakdown(self):
        qs = self._base_qs()
        raw = (
            qs.values("category__name_en")
            .annotate(count=Count("id"))
            .order_by("-count")
        )
        return [
            {"name": r["category__name_en"] or "Uncategorized", "count": r["count"]}
            for r in raw
        ]

    def get_department_breakdown(self):
        qs = self._base_qs()
        dept_data = defaultdict(lambda: {"total": 0, "resolved": 0, "open": 0, "in_progress": 0})
        for obs in qs.iterator(chunk_size=2000):
            name = obs.assigned_department.name if obs.assigned_department else "Unassigned"
            dept_data[name]["total"] += 1
            if obs.status in ("resolved", "closed"):
                dept_data[name]["resolved"] += 1
            elif obs.status == "in_progress":
                dept_data[name]["in_progress"] += 1
            else:
                dept_data[name]["open"] += 1
        return sorted(
            [{"name": name, **counts} for name, counts in dept_data.items()],
            key=lambda x: x["total"],
            reverse=True,
        )

    def get_employee_breakdown(self):
        qs = self._base_qs()
        emp_data = defaultdict(lambda: {"total": 0, "resolved": 0, "open": 0, "in_progress": 0})
        for obs in qs.iterator(chunk_size=2000):
            name = obs.assigned_to.get_full_name() if obs.assigned_to else "Unassigned"
            emp_data[name]["total"] += 1
            if obs.status in ("resolved", "closed"):
                emp_data[name]["resolved"] += 1
            elif obs.status == "in_progress":
                emp_data[name]["in_progress"] += 1
            else:
                emp_data[name]["open"] += 1
        return sorted(
            [{"name": name, **counts} for name, counts in emp_data.items()],
            key=lambda x: x["total"],
            reverse=True,
        )

    def get_chart_data(self):
        daily = self.get_daily_trend()
        summary = self.get_summary()

        by_status_chart = [
            {"x": STATUS_LABELS.get(s, s), "y": c}
            for s, c in summary["by_status"].items()
        ]

        by_severity_chart = [
            {
                "x": SEVERITY_LABELS.get(s, s),
                "y": c,
                "color": SEVERITY_COLORS.get(s, "#6B7280"),
            }
            for s, c in sorted(
                summary["by_severity"].items(),
                key=lambda x: SEVERITY_ORDER.index(x[0]) if x[0] in SEVERITY_ORDER else 99,
            )
        ]

        by_source_chart = [
            {"x": SOURCE_MAP.get(s, s), "y": c}
            for s, c in sorted(summary["by_source"].items(), key=lambda x: -x[1])
        ]

        category = self.get_category_breakdown()
        by_category_chart = [
            {"x": c["name"], "y": c["count"]}
            for c in category
        ]

        return {
            "daily": [{"x": d["label"], "y": d["count"]} for d in daily],
            "by_status": by_status_chart,
            "by_severity": by_severity_chart,
            "by_source": by_source_chart,
            "by_category": by_category_chart,
        }
