"""Auswertungen – Kampagnen, Recherchen, Veranstaltungen, Spenden."""

import csv
import io
from datetime import datetime
from typing import Optional

from fastapi import APIRouter, Depends, Request
from fastapi.responses import HTMLResponse, RedirectResponse, StreamingResponse
from fastapi.templating import Jinja2Templates
from sqlalchemy.orm import Session, joinedload

from dependencies import get_current_user, get_db
from models.aktivitaet import Aktivitaet
from models.kampagne import Kampagne, KampagneKontakt
from models.recherche import GespeicherteRecherche
from models.spende import Spende
from models.veranstaltung import Veranstaltung, Teilnehmer

router = APIRouter(prefix="/auswertungen")
templates = Jinja2Templates(directory="templates")


# ─── Datenfunktionen ────────────────────────────────────────────────────────

def _kampagnen_daten(db: Session, status_filter: str = None):
    q = (
        db.query(Kampagne)
        .options(
            joinedload(Kampagne.kontakte),
            joinedload(Kampagne.spenden),
            joinedload(Kampagne.veranstaltungen),
        )
        .order_by(Kampagne.erstellt_am.desc())
    )
    if status_filter:
        q = q.filter(Kampagne.status == status_filter)
    kampagnen = q.all()

    rows = []
    for k in kampagnen:
        gesamt = len(k.kontakte)
        sc = {}
        for kk in k.kontakte:
            sc[kk.status] = sc.get(kk.status, 0) + 1

        def pct(n, g=gesamt):
            return round(n / g * 100) if g > 0 else 0

        n_neu = sc.get("neu", 0)
        n_kt = sc.get("kontaktiert", 0)
        n_rg = sc.get("reagiert", 0)
        n_ab = sc.get("abgelehnt", 0)
        n_er = sc.get("erledigt", 0)
        spenden_summe = sum(s.betrag for s in k.spenden) if k.spenden else 0.0

        rows.append({
            "kampagne": k,
            "anzahl_kontakte": gesamt,
            "spenden_summe": spenden_summe,
            "anzahl_veranstaltungen": len(k.veranstaltungen),
            "status_neu": n_neu,
            "status_kontaktiert": n_kt,
            "status_reagiert": n_rg,
            "status_abgelehnt": n_ab,
            "status_erledigt": n_er,
            "erfolgsquote": pct(n_rg + n_er),
            "pct_neu": pct(n_neu),
            "pct_kontaktiert": pct(n_kt),
            "pct_reagiert": pct(n_rg),
            "pct_abgelehnt": pct(n_ab),
            "pct_erledigt": pct(n_er),
        })
    return rows


def _veranstaltungen_rows(
    db: Session,
    status_filter: str = None,
    kampagne_id_filter: int = None,
    datum_von=None,
    datum_bis=None,
):
    q = (
        db.query(Veranstaltung)
        .options(joinedload(Veranstaltung.teilnehmer), joinedload(Veranstaltung.kampagne))
        .order_by(Veranstaltung.start_datum.desc())
    )
    if status_filter:
        q = q.filter(Veranstaltung.status == status_filter)
    if kampagne_id_filter:
        q = q.filter(Veranstaltung.kampagne_id == kampagne_id_filter)
    if datum_von:
        q = q.filter(Veranstaltung.start_datum >= datum_von)
    if datum_bis:
        q = q.filter(Veranstaltung.start_datum <= datum_bis)

    rows = []
    for v in q.all():
        tn_status = {}
        tn_rollen = {}
        for tn in v.teilnehmer:
            tn_status[tn.status] = tn_status.get(tn.status, 0) + 1
            tn_rollen[tn.rolle] = tn_rollen.get(tn.rolle, 0) + 1

        gesamt = len(v.teilnehmer)

        def pct(n, g=gesamt):
            return round(n / g * 100) if g > 0 else 0

        n_ang = tn_status.get("angemeldet", 0)
        n_tg = tn_status.get("teilgenommen", 0)
        n_abs = tn_status.get("abgesagt", 0)
        auslastung = (
            round(gesamt / v.max_teilnehmer * 100)
            if v.max_teilnehmer and v.max_teilnehmer > 0
            else None
        )
        rows.append({
            "veranstaltung": v,
            "anzahl_teilnehmer": gesamt,
            "tn_angemeldet": n_ang,
            "tn_teilgenommen": n_tg,
            "tn_abgesagt": n_abs,
            "pct_angemeldet": pct(n_ang),
            "pct_teilgenommen": pct(n_tg),
            "pct_abgesagt": pct(n_abs),
            "tn_referenten": tn_rollen.get("referent", 0),
            "tn_organisatoren": tn_rollen.get("organisator", 0),
            "auslastung_pct": auslastung,
        })
    return rows


def _recherchen_daten(db: Session, benutzer_filter: str = None):
    q = db.query(GespeicherteRecherche).order_by(GespeicherteRecherche.erstellt_am.desc())
    if benutzer_filter:
        q = q.filter(GespeicherteRecherche.erstellt_von == benutzer_filter)
    return q.all()


def _spenden_daten(db: Session, kampagne_id_filter: int = None, datum_von=None, datum_bis=None):
    q = (
        db.query(Spende)
        .options(joinedload(Spende.kontakt), joinedload(Spende.kampagne))
        .order_by(Spende.datum.desc())
    )
    if kampagne_id_filter:
        q = q.filter(Spende.kampagne_id == kampagne_id_filter)
    if datum_von:
        q = q.filter(Spende.datum >= datum_von)
    if datum_bis:
        q = q.filter(Spende.datum <= datum_bis)
    return q.all()


def _spenden_pro_kampagne(spenden):
    ohne = []
    mit = {}
    for s in spenden:
        if s.kampagne_id:
            if s.kampagne_id not in mit:
                mit[s.kampagne_id] = {"kampagne": s.kampagne, "betrag": 0.0, "anzahl": 0}
            mit[s.kampagne_id]["betrag"] += s.betrag
            mit[s.kampagne_id]["anzahl"] += 1
        else:
            ohne.append(s)
    rows = sorted(mit.values(), key=lambda r: r["betrag"], reverse=True)
    if ohne:
        rows.append({
            "kampagne": None,
            "betrag": sum(s.betrag for s in ohne),
            "anzahl": len(ohne),
        })
    return rows


# ─── CSV-Exporte (müssen VOR den /{id}-Routen stehen) ───────────────────────

@router.get("/kampagnen/csv")
async def kampagnen_csv(user=Depends(get_current_user), db: Session = Depends(get_db)):
    rows = _kampagnen_daten(db)
    output = io.StringIO()
    writer = csv.writer(output, delimiter=";")
    writer.writerow([
        "Titel", "Typ", "Status", "Projekt",
        "Kontakte Gesamt", "Neu", "Kontaktiert", "Reagiert", "Abgelehnt", "Erledigt",
        "Erfolgsquote %", "Spenden EUR", "Veranstaltungen", "Erstellt am",
    ])
    for r in rows:
        k = r["kampagne"]
        writer.writerow([
            k.titel, k.typ_label, k.status_label, k.projekt or "",
            r["anzahl_kontakte"], r["status_neu"], r["status_kontaktiert"],
            r["status_reagiert"], r["status_abgelehnt"], r["status_erledigt"],
            r["erfolgsquote"],
            f"{r['spenden_summe']:.2f}", r["anzahl_veranstaltungen"],
            k.erstellt_am.strftime("%d.%m.%Y") if k.erstellt_am else "",
        ])
    content = output.getvalue()
    return StreamingResponse(
        io.BytesIO(content.encode("utf-8-sig")),
        media_type="text/csv; charset=utf-8",
        headers={"Content-Disposition": "attachment; filename=auswertung_kampagnen.csv"},
    )


@router.get("/recherchen/csv")
async def recherchen_csv(user=Depends(get_current_user), db: Session = Depends(get_db)):
    recherchen = _recherchen_daten(db)
    output = io.StringIO()
    writer = csv.writer(output, delimiter=";")
    writer.writerow(["Name", "Anzahl Kontakte", "Erstellt am", "Von"])
    for r in recherchen:
        writer.writerow([
            r.name,
            r.anzahl_kontakte,
            r.erstellt_am.strftime("%d.%m.%Y") if r.erstellt_am else "",
            r.erstellt_von or "",
        ])
    content = output.getvalue()
    return StreamingResponse(
        io.BytesIO(content.encode("utf-8-sig")),
        media_type="text/csv; charset=utf-8",
        headers={"Content-Disposition": "attachment; filename=auswertung_recherchen.csv"},
    )


@router.get("/veranstaltungen/csv")
async def veranstaltungen_csv(user=Depends(get_current_user), db: Session = Depends(get_db)):
    rows = _veranstaltungen_rows(db)
    output = io.StringIO()
    writer = csv.writer(output, delimiter=";")
    writer.writerow([
        "Titel", "Datum", "Ende", "Ort", "Typ", "Status",
        "Teilnehmer Gesamt", "Angemeldet", "Teilgenommen", "Abgesagt",
        "Referenten", "Max. Teilnehmer", "Auslastung %", "Kampagne",
    ])
    for r in rows:
        v = r["veranstaltung"]
        writer.writerow([
            v.titel,
            v.start_datum.strftime("%d.%m.%Y") if v.start_datum else "",
            v.end_datum.strftime("%d.%m.%Y") if v.end_datum else "",
            v.ort or "",
            v.typ or "",
            v.status or "",
            r["anzahl_teilnehmer"],
            r["tn_angemeldet"],
            r["tn_teilgenommen"],
            r["tn_abgesagt"],
            r["tn_referenten"],
            v.max_teilnehmer or "",
            r["auslastung_pct"] if r["auslastung_pct"] is not None else "",
            v.kampagne.titel if v.kampagne else "",
        ])
    content = output.getvalue()
    return StreamingResponse(
        io.BytesIO(content.encode("utf-8-sig")),
        media_type="text/csv; charset=utf-8",
        headers={"Content-Disposition": "attachment; filename=auswertung_veranstaltungen.csv"},
    )


@router.get("/spenden/csv")
async def spenden_csv(user=Depends(get_current_user), db: Session = Depends(get_db)):
    spenden = _spenden_daten(db)
    output = io.StringIO()
    writer = csv.writer(output, delimiter=";")
    writer.writerow([
        "Datum", "Kontakt", "Betrag EUR", "Typ", "Zweck",
        "Kampagne", "Projekt", "Zahlungsart", "Quittung",
    ])
    for s in spenden:
        writer.writerow([
            s.datum.strftime("%d.%m.%Y") if s.datum else "",
            s.kontakt.anzeigename if s.kontakt else "",
            f"{s.betrag:.2f}",
            s.typ or "",
            s.zweck or "",
            s.kampagne.titel if s.kampagne else "",
            s.projekt or "",
            s.zahlungsart or "",
            s.quittung or "",
        ])
    content = output.getvalue()
    return StreamingResponse(
        io.BytesIO(content.encode("utf-8-sig")),
        media_type="text/csv; charset=utf-8",
        headers={"Content-Disposition": "attachment; filename=auswertung_spenden.csv"},
    )


# ─── Übersicht ───────────────────────────────────────────────────────────────

@router.get("", response_class=HTMLResponse)
async def index(
    request: Request,
    user=Depends(get_current_user),
    db: Session = Depends(get_db),
    k_status: str = "",
    v_status: str = "",
    v_kampagne_id: Optional[int] = None,
    r_benutzer: str = "",
    datum_von: str = "",
    datum_bis: str = "",
):
    datum_von_dt = datetime.strptime(datum_von, "%Y-%m-%d") if datum_von else None
    datum_bis_dt = datetime.strptime(datum_bis, "%Y-%m-%d") if datum_bis else None

    kampagnen_rows = _kampagnen_daten(db, status_filter=k_status or None)
    recherchen = _recherchen_daten(db, benutzer_filter=r_benutzer or None)
    v_rows = _veranstaltungen_rows(
        db,
        status_filter=v_status or None,
        kampagne_id_filter=v_kampagne_id,
        datum_von=datum_von_dt,
        datum_bis=datum_bis_dt,
    )
    spenden = _spenden_daten(db)
    spenden_pro_kampagne = _spenden_pro_kampagne(spenden)

    alle_kampagnen = db.query(Kampagne).order_by(Kampagne.titel).all()
    alle_benutzer = [
        r[0]
        for r in db.query(GespeicherteRecherche.erstellt_von)
        .filter(GespeicherteRecherche.erstellt_von.isnot(None))
        .distinct()
        .all()
    ]

    k_gesamt = len(kampagnen_rows)
    k_aktiv = sum(1 for r in kampagnen_rows if r["kampagne"].status == "aktiv")
    k_kontakte = sum(r["anzahl_kontakte"] for r in kampagnen_rows)
    k_spenden = sum(r["spenden_summe"] for r in kampagnen_rows)
    v_gesamt = len(v_rows)
    v_teilnehmer = sum(r["anzahl_teilnehmer"] for r in v_rows)
    sp_gesamt = sum(s.betrag for s in spenden)
    sp_anzahl = len(spenden)
    sp_avg = sp_gesamt / sp_anzahl if sp_anzahl > 0 else 0.0

    return templates.TemplateResponse("pages/auswertungen/index.html", {
        "request": request,
        "user": user,
        "active_page": "auswertungen",
        "kampagnen_rows": kampagnen_rows,
        "recherchen": recherchen,
        "v_rows": v_rows,
        "spenden": spenden,
        "spenden_pro_kampagne": spenden_pro_kampagne,
        "alle_kampagnen": alle_kampagnen,
        "alle_benutzer": alle_benutzer,
        "k_gesamt": k_gesamt,
        "k_aktiv": k_aktiv,
        "k_kontakte": k_kontakte,
        "k_spenden": k_spenden,
        "v_gesamt": v_gesamt,
        "v_teilnehmer": v_teilnehmer,
        "sp_gesamt": sp_gesamt,
        "sp_anzahl": sp_anzahl,
        "sp_avg": sp_avg,
        "f_k_status": k_status,
        "f_v_status": v_status,
        "f_v_kampagne_id": v_kampagne_id,
        "f_r_benutzer": r_benutzer,
        "f_datum_von": datum_von,
        "f_datum_bis": datum_bis,
    })


# ─── Einzelauswertungen ──────────────────────────────────────────────────────

@router.get("/kampagnen/{k_id}", response_class=HTMLResponse)
async def kampagne_detail(
    k_id: int,
    request: Request,
    user=Depends(get_current_user),
    db: Session = Depends(get_db),
):
    k = (
        db.query(Kampagne)
        .options(
            joinedload(Kampagne.kontakte).joinedload(KampagneKontakt.kontakt),
            joinedload(Kampagne.spenden).joinedload(Spende.kontakt),
            joinedload(Kampagne.veranstaltungen),
        )
        .filter(Kampagne.id == k_id)
        .first()
    )
    if not k:
        return RedirectResponse("/auswertungen")

    gesamt = len(k.kontakte)
    sc = {}
    for kk in k.kontakte:
        sc[kk.status] = sc.get(kk.status, 0) + 1

    def pct(n, g=gesamt):
        return round(n / g * 100) if g > 0 else 0

    n_neu = sc.get("neu", 0)
    n_kt = sc.get("kontaktiert", 0)
    n_rg = sc.get("reagiert", 0)
    n_ab = sc.get("abgelehnt", 0)
    n_er = sc.get("erledigt", 0)
    spenden_summe = sum(s.betrag for s in k.spenden) if k.spenden else 0.0
    sp_avg = spenden_summe / len(k.spenden) if k.spenden else 0.0

    # Kontakte sortiert nach Status
    kontakte_sortiert = sorted(k.kontakte, key=lambda kk: kk.status)

    aktivitaeten = (
        db.query(Aktivitaet)
        .filter(Aktivitaet.kampagne_id == k_id)
        .order_by(Aktivitaet.datum.desc())
        .all()
    )

    return templates.TemplateResponse("pages/auswertungen/kampagne_detail.html", {
        "request": request,
        "user": user,
        "active_page": "auswertungen",
        "kampagne": k,
        "gesamt": gesamt,
        "n_neu": n_neu,
        "n_kontaktiert": n_kt,
        "n_reagiert": n_rg,
        "n_abgelehnt": n_ab,
        "n_erledigt": n_er,
        "pct_neu": pct(n_neu),
        "pct_kontaktiert": pct(n_kt),
        "pct_reagiert": pct(n_rg),
        "pct_abgelehnt": pct(n_ab),
        "pct_erledigt": pct(n_er),
        "erfolgsquote": pct(n_rg + n_er),
        "spenden_summe": spenden_summe,
        "sp_avg": sp_avg,
        "kontakte_sortiert": kontakte_sortiert,
        "aktivitaeten": aktivitaeten,
    })


@router.get("/veranstaltungen/{v_id}", response_class=HTMLResponse)
async def veranstaltung_detail(
    v_id: int,
    request: Request,
    user=Depends(get_current_user),
    db: Session = Depends(get_db),
):
    v = (
        db.query(Veranstaltung)
        .options(
            joinedload(Veranstaltung.teilnehmer).joinedload(Teilnehmer.kontakt),
            joinedload(Veranstaltung.kampagne),
        )
        .filter(Veranstaltung.id == v_id)
        .first()
    )
    if not v:
        return RedirectResponse("/auswertungen")

    tn_status = {}
    tn_rollen = {}
    for tn in v.teilnehmer:
        tn_status[tn.status] = tn_status.get(tn.status, 0) + 1
        tn_rollen[tn.rolle] = tn_rollen.get(tn.rolle, 0) + 1

    gesamt = len(v.teilnehmer)

    def pct(n, g=gesamt):
        return round(n / g * 100) if g > 0 else 0

    n_ang = tn_status.get("angemeldet", 0)
    n_tg = tn_status.get("teilgenommen", 0)
    n_abs = tn_status.get("abgesagt", 0)
    auslastung = (
        round(gesamt / v.max_teilnehmer * 100)
        if v.max_teilnehmer and v.max_teilnehmer > 0
        else None
    )

    # Teilnehmer sortiert: Referenten + Organisatoren zuerst, dann nach Status
    def tn_sort_key(tn):
        rolle_order = {"referent": 0, "organisator": 1, "teilnehmer": 2}
        status_order = {"teilgenommen": 0, "angemeldet": 1, "abgesagt": 2}
        return (rolle_order.get(tn.rolle, 9), status_order.get(tn.status, 9))

    teilnehmer_sortiert = sorted(v.teilnehmer, key=tn_sort_key)

    return templates.TemplateResponse("pages/auswertungen/veranstaltung_detail.html", {
        "request": request,
        "user": user,
        "active_page": "auswertungen",
        "veranstaltung": v,
        "gesamt": gesamt,
        "n_angemeldet": n_ang,
        "n_teilgenommen": n_tg,
        "n_abgesagt": n_abs,
        "pct_angemeldet": pct(n_ang),
        "pct_teilgenommen": pct(n_tg),
        "pct_abgesagt": pct(n_abs),
        "tn_referenten": tn_rollen.get("referent", 0),
        "tn_organisatoren": tn_rollen.get("organisator", 0),
        "auslastung_pct": auslastung,
        "teilnehmer_sortiert": teilnehmer_sortiert,
    })
