import csv
import io
from datetime import date, datetime

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

from dependencies import get_current_user, get_db
from models.kampagne import Kampagne
from models.kontakt import Kontakt
from models.spende import Spende

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


@router.get("", response_class=HTMLResponse)
async def liste(
    request: Request,
    seite: int = 1,
    jahr: int = None,
    quittung: str = None,
    kampagne_id: int = None,
    q: str = None,
    kontakt_q: str = None,
    user=Depends(get_current_user),
    db: Session = Depends(get_db),
):
    pro_seite = 50
    query = db.query(Spende)

    if jahr:
        query = query.filter(extract("year", Spende.datum) == jahr)
    if quittung:
        query = query.filter(Spende.quittung == quittung)
    if kampagne_id:
        query = query.filter(Spende.kampagne_id == kampagne_id)

    if q:
        from sqlalchemy import or_
        like = f"%{q}%"
        query = query.filter(or_(
            Spende.zweck.ilike(like),
            Spende.bemerkung.ilike(like),
        ))

    if kontakt_q:
        from sqlalchemy import or_
        klike = f"%{kontakt_q}%"
        matching_ids = db.query(Kontakt.id).filter(or_(
            Kontakt.nachname.ilike(klike),
            Kontakt.vorname.ilike(klike),
            Kontakt.firmenname.ilike(klike),
        )).subquery()
        query = query.filter(Spende.kontakt_id.in_(matching_ids))

    gesamt = query.count()
    spenden = (
        query.order_by(Spende.datum.desc())
        .offset((seite - 1) * pro_seite)
        .limit(pro_seite)
        .all()
    )
    seiten = (gesamt + pro_seite - 1) // pro_seite
    alle_kampagnen = db.query(Kampagne).order_by(Kampagne.titel).all()

    return templates.TemplateResponse("pages/spenden/liste.html", {
        "request": request,
        "user": user,
        "active_page": "spenden",
        "spenden": spenden,
        "gesamt": gesamt,
        "seite": seite,
        "seiten": seiten,
        "jahr_filter": jahr,
        "quittung_filter": quittung,
        "kampagne_filter": kampagne_id,
        "alle_kampagnen": alle_kampagnen,
        "q": q,
        "kontakt_q": kontakt_q,
    })


@router.get("/neu", response_class=HTMLResponse)
async def neu_formular(request: Request, kontakt_id: int = None, user=Depends(get_current_user), db: Session = Depends(get_db)):
    kontakte = db.query(Kontakt).order_by(Kontakt.nachname, Kontakt.vorname).all()
    alle_kampagnen = db.query(Kampagne).order_by(Kampagne.titel).all()
    return templates.TemplateResponse("pages/spenden/formular.html", {
        "request": request,
        "user": user,
        "active_page": "spenden",
        "spende": None,
        "kontakte": kontakte,
        "kontakt_id": kontakt_id,
        "alle_kampagnen": alle_kampagnen,
    })


@router.post("/neu")
async def erstellen(request: Request, user=Depends(get_current_user), db: Session = Depends(get_db)):
    form = await request.form()
    kampagne_id_raw = form.get("kampagne_id")
    spende = Spende(
        kontakt_id=int(form["kontakt_id"]),
        betrag=float(form["betrag"]),
        datum=date.fromisoformat(form["datum"]),
        typ=form.get("typ", "einzelspende"),
        zweck=form.get("zweck") or None,
        projekt=form.get("projekt") or None,
        kampagne_id=int(kampagne_id_raw) if kampagne_id_raw else None,
        quittung=form.get("quittung", "keine"),
        zahlungsart=form.get("zahlungsart", "ueberweisung"),
        bemerkung=form.get("bemerkung") or None,
        erfasst_von=user.kuerzel,
    )
    db.add(spende)
    db.commit()
    next_url = form.get("next") or "/spenden"
    return RedirectResponse(url=next_url, status_code=303)


@router.get("/{spende_id}/bearbeiten", response_class=HTMLResponse)
async def bearbeiten_formular(spende_id: int, request: Request, user=Depends(get_current_user), db: Session = Depends(get_db)):
    spende = db.query(Spende).filter(Spende.id == spende_id).first()
    if not spende:
        return RedirectResponse(url="/spenden", status_code=303)

    kontakte = db.query(Kontakt).order_by(Kontakt.nachname, Kontakt.vorname).all()
    alle_kampagnen = db.query(Kampagne).order_by(Kampagne.titel).all()
    return templates.TemplateResponse("pages/spenden/formular.html", {
        "request": request,
        "user": user,
        "active_page": "spenden",
        "spende": spende,
        "kontakte": kontakte,
        "kontakt_id": spende.kontakt_id,
        "alle_kampagnen": alle_kampagnen,
    })


@router.post("/{spende_id}/bearbeiten")
async def aktualisieren(spende_id: int, request: Request, user=Depends(get_current_user), db: Session = Depends(get_db)):
    spende = db.query(Spende).filter(Spende.id == spende_id).first()
    if not spende:
        return RedirectResponse(url="/spenden", status_code=303)

    form = await request.form()
    kampagne_id_raw = form.get("kampagne_id")
    spende.kontakt_id = int(form["kontakt_id"])
    spende.betrag = float(form["betrag"])
    spende.datum = date.fromisoformat(form["datum"])
    spende.typ = form.get("typ", "einzelspende")
    spende.zweck = form.get("zweck") or None
    spende.projekt = form.get("projekt") or None
    spende.kampagne_id = int(kampagne_id_raw) if kampagne_id_raw else None
    spende.quittung = form.get("quittung", "keine")
    spende.zahlungsart = form.get("zahlungsart", "ueberweisung")
    spende.bemerkung = form.get("bemerkung") or None
    db.commit()
    next_url = form.get("next") or "/spenden"
    return RedirectResponse(url=next_url, status_code=303)


@router.get("/{spende_id}/loeschen")
async def loeschen(spende_id: int, user=Depends(get_current_user), db: Session = Depends(get_db)):
    spende = db.query(Spende).filter(Spende.id == spende_id).first()
    if spende:
        db.delete(spende)
        db.commit()
    return RedirectResponse(url="/spenden", status_code=303)


@router.get("/bericht", response_class=HTMLResponse)
async def bericht(request: Request, user=Depends(get_current_user), db: Session = Depends(get_db)):
    gesamt_summe = db.query(func.sum(Spende.betrag)).scalar() or 0
    anzahl_spenden = db.query(Spende).count()
    anzahl_spender = db.query(func.count(func.distinct(Spende.kontakt_id))).scalar() or 0

    pro_jahr = (
        db.query(
            extract("year", Spende.datum).label("jahr"),
            func.sum(Spende.betrag).label("summe"),
            func.count().label("anzahl"),
        )
        .group_by("jahr")
        .order_by(extract("year", Spende.datum).desc())
        .all()
    )

    pro_projekt = (
        db.query(
            Spende.projekt.label("projekt"),
            func.sum(Spende.betrag).label("summe"),
            func.count().label("anzahl"),
        )
        .group_by(Spende.projekt)
        .order_by(func.sum(Spende.betrag).desc())
        .all()
    )

    # Pro Kampagne
    kampagnen_map = {k.id: k.titel for k in db.query(Kampagne).all()}
    pro_kampagne_rows = (
        db.query(
            Spende.kampagne_id,
            func.sum(Spende.betrag).label("summe"),
            func.count().label("anzahl"),
        )
        .filter(Spende.kampagne_id.isnot(None))
        .group_by(Spende.kampagne_id)
        .order_by(func.sum(Spende.betrag).desc())
        .all()
    )
    pro_kampagne = [
        {"kampagne_id": r.kampagne_id, "titel": kampagnen_map.get(r.kampagne_id, f"#{r.kampagne_id}"), "summe": r.summe, "anzahl": r.anzahl}
        for r in pro_kampagne_rows
    ]

    return templates.TemplateResponse("pages/spenden/bericht.html", {
        "request": request,
        "user": user,
        "active_page": "spenden",
        "gesamt_summe": gesamt_summe,
        "anzahl_spenden": anzahl_spenden,
        "anzahl_spender": anzahl_spender,
        "pro_jahr": pro_jahr,
        "pro_projekt": pro_projekt,
        "pro_kampagne": pro_kampagne,
    })


@router.get("/bericht/export/csv")
async def bericht_export_csv(user=Depends(get_current_user), db: Session = Depends(get_db)):
    spenden = (
        db.query(Spende)
        .options(joinedload(Spende.kontakt), joinedload(Spende.kampagne))
        .order_by(Spende.datum.desc())
        .all()
    )

    output = io.StringIO()
    writer = csv.writer(output, delimiter=";")
    writer.writerow(["Datum", "Kontakt", "Betrag EUR", "Zweck", "Projekt", "Kampagne", "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.zweck or "",
            s.projekt or "",
            s.kampagne.titel if s.kampagne else "",
            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=spendenbericht.csv"},
    )
