"""
FastAPI routes for Einwilligungen — Consent-Tracking, Cookie-Banner und Datenpunktkatalog.

Endpoints:
  GET    /einwilligungen/catalog              — Katalog laden
  PUT    /einwilligungen/catalog              — Katalog speichern (Upsert by tenant_id)
  GET    /einwilligungen/company              — Firmeninfo laden
  PUT    /einwilligungen/company              — Firmeninfo speichern (Upsert)
  GET    /einwilligungen/cookies              — Cookie-Banner-Config laden
  PUT    /einwilligungen/cookies              — Cookie-Banner-Config speichern (Upsert)
  GET    /einwilligungen/consents             — Consent-Liste (Pagination + Filter)
  POST   /einwilligungen/consents             — Consent erfassen
  PUT    /einwilligungen/consents/{id}/revoke — Consent widerrufen
  GET    /einwilligungen/consents/stats       — Statistiken
"""

import logging
from datetime import datetime, timezone
from typing import Optional, List, Any, Dict

from fastapi import APIRouter, Depends, HTTPException, Query, Header
from pydantic import BaseModel
from sqlalchemy.orm import Session

from classroom_engine.database import get_db
from ..db.einwilligungen_models import (
    EinwilligungenCatalogDB,
    EinwilligungenCompanyDB,
    EinwilligungenCookiesDB,
    EinwilligungenConsentDB,
    EinwilligungenConsentHistoryDB,
)

logger = logging.getLogger(__name__)
router = APIRouter(prefix="/einwilligungen", tags=["einwilligungen"])


# ============================================================================
# Pydantic Schemas
# ============================================================================

class CatalogUpsert(BaseModel):
    selected_data_point_ids: List[str] = []
    custom_data_points: List[Dict[str, Any]] = []


class CompanyUpsert(BaseModel):
    data: Dict[str, Any] = {}


class CookiesUpsert(BaseModel):
    categories: List[Dict[str, Any]] = []
    config: Dict[str, Any] = {}


class ConsentCreate(BaseModel):
    user_id: str
    data_point_id: str
    granted: bool
    consent_version: str = '1.0'
    source: Optional[str] = None
    ip_address: Optional[str] = None
    user_agent: Optional[str] = None


# ============================================================================
# Helpers
# ============================================================================

def _get_tenant(x_tenant_id: Optional[str] = Header(None, alias='X-Tenant-ID')) -> str:
    if not x_tenant_id:
        raise HTTPException(status_code=400, detail="X-Tenant-ID header required")
    return x_tenant_id


def _record_history(db: Session, consent: EinwilligungenConsentDB, action: str) -> None:
    """Protokolliert eine Aenderung an einer Einwilligung in der History-Tabelle."""
    entry = EinwilligungenConsentHistoryDB(
        consent_id=consent.id,
        tenant_id=consent.tenant_id,
        action=action,
        consent_version=consent.consent_version,
        ip_address=consent.ip_address,
        user_agent=consent.user_agent,
        source=consent.source,
    )
    db.add(entry)


# ============================================================================
# Catalog
# ============================================================================

@router.get("/catalog")
async def get_catalog(
    tenant_id: str = Depends(_get_tenant),
    db: Session = Depends(get_db),
):
    """Load the data point catalog for a tenant."""
    record = db.query(EinwilligungenCatalogDB).filter(
        EinwilligungenCatalogDB.tenant_id == tenant_id
    ).first()

    if not record:
        return {
            "tenant_id": tenant_id,
            "selected_data_point_ids": [],
            "custom_data_points": [],
            "updated_at": None,
        }

    return {
        "tenant_id": tenant_id,
        "selected_data_point_ids": record.selected_data_point_ids or [],
        "custom_data_points": record.custom_data_points or [],
        "updated_at": record.updated_at,
    }


@router.put("/catalog")
async def upsert_catalog(
    request: CatalogUpsert,
    tenant_id: str = Depends(_get_tenant),
    db: Session = Depends(get_db),
):
    """Create or update the data point catalog for a tenant."""
    record = db.query(EinwilligungenCatalogDB).filter(
        EinwilligungenCatalogDB.tenant_id == tenant_id
    ).first()

    if record:
        record.selected_data_point_ids = request.selected_data_point_ids
        record.custom_data_points = request.custom_data_points
        record.updated_at = datetime.now(timezone.utc)
    else:
        record = EinwilligungenCatalogDB(
            tenant_id=tenant_id,
            selected_data_point_ids=request.selected_data_point_ids,
            custom_data_points=request.custom_data_points,
        )
        db.add(record)

    db.commit()
    db.refresh(record)
    return {
        "success": True,
        "tenant_id": tenant_id,
        "selected_data_point_ids": record.selected_data_point_ids,
        "custom_data_points": record.custom_data_points,
        "updated_at": record.updated_at,
    }


# ============================================================================
# Company Info
# ============================================================================

@router.get("/company")
async def get_company(
    tenant_id: str = Depends(_get_tenant),
    db: Session = Depends(get_db),
):
    """Load company information for DSI generation."""
    record = db.query(EinwilligungenCompanyDB).filter(
        EinwilligungenCompanyDB.tenant_id == tenant_id
    ).first()

    if not record:
        return {"tenant_id": tenant_id, "data": {}, "updated_at": None}

    return {"tenant_id": tenant_id, "data": record.data or {}, "updated_at": record.updated_at}


@router.put("/company")
async def upsert_company(
    request: CompanyUpsert,
    tenant_id: str = Depends(_get_tenant),
    db: Session = Depends(get_db),
):
    """Create or update company information for a tenant."""
    record = db.query(EinwilligungenCompanyDB).filter(
        EinwilligungenCompanyDB.tenant_id == tenant_id
    ).first()

    if record:
        record.data = request.data
        record.updated_at = datetime.now(timezone.utc)
    else:
        record = EinwilligungenCompanyDB(tenant_id=tenant_id, data=request.data)
        db.add(record)

    db.commit()
    db.refresh(record)
    return {"success": True, "tenant_id": tenant_id, "data": record.data, "updated_at": record.updated_at}


# ============================================================================
# Cookie Banner Config
# ============================================================================

@router.get("/cookies")
async def get_cookies(
    tenant_id: str = Depends(_get_tenant),
    db: Session = Depends(get_db),
):
    """Load cookie banner configuration for a tenant."""
    record = db.query(EinwilligungenCookiesDB).filter(
        EinwilligungenCookiesDB.tenant_id == tenant_id
    ).first()

    if not record:
        return {"tenant_id": tenant_id, "categories": [], "config": {}, "updated_at": None}

    return {
        "tenant_id": tenant_id,
        "categories": record.categories or [],
        "config": record.config or {},
        "updated_at": record.updated_at,
    }


@router.put("/cookies")
async def upsert_cookies(
    request: CookiesUpsert,
    tenant_id: str = Depends(_get_tenant),
    db: Session = Depends(get_db),
):
    """Create or update cookie banner configuration for a tenant."""
    record = db.query(EinwilligungenCookiesDB).filter(
        EinwilligungenCookiesDB.tenant_id == tenant_id
    ).first()

    if record:
        record.categories = request.categories
        record.config = request.config
        record.updated_at = datetime.now(timezone.utc)
    else:
        record = EinwilligungenCookiesDB(
            tenant_id=tenant_id,
            categories=request.categories,
            config=request.config,
        )
        db.add(record)

    db.commit()
    db.refresh(record)
    return {
        "success": True,
        "tenant_id": tenant_id,
        "categories": record.categories,
        "config": record.config,
        "updated_at": record.updated_at,
    }


# ============================================================================
# Consents
# ============================================================================

@router.get("/consents/stats")
async def get_consent_stats(
    tenant_id: str = Depends(_get_tenant),
    db: Session = Depends(get_db),
):
    """Get consent statistics for a tenant."""
    all_consents = db.query(EinwilligungenConsentDB).filter(
        EinwilligungenConsentDB.tenant_id == tenant_id
    ).all()

    total = len(all_consents)
    active = sum(1 for c in all_consents if c.granted and not c.revoked_at)
    revoked = sum(1 for c in all_consents if c.revoked_at)

    # Unique users
    unique_users = len(set(c.user_id for c in all_consents))
    users_with_active = len(set(c.user_id for c in all_consents if c.granted and not c.revoked_at))
    conversion_rate = round((users_with_active / unique_users * 100), 1) if unique_users > 0 else 0.0

    # By data point
    by_data_point: Dict[str, Dict] = {}
    for c in all_consents:
        dp = c.data_point_id
        if dp not in by_data_point:
            by_data_point[dp] = {"total": 0, "active": 0, "revoked": 0}
        by_data_point[dp]["total"] += 1
        if c.granted and not c.revoked_at:
            by_data_point[dp]["active"] += 1
        if c.revoked_at:
            by_data_point[dp]["revoked"] += 1

    return {
        "total_consents": total,
        "active_consents": active,
        "revoked_consents": revoked,
        "unique_users": unique_users,
        "conversion_rate": conversion_rate,
        "by_data_point": by_data_point,
    }


@router.get("/consents")
async def list_consents(
    tenant_id: str = Depends(_get_tenant),
    user_id: Optional[str] = Query(None),
    data_point_id: Optional[str] = Query(None),
    granted: Optional[bool] = Query(None),
    limit: int = Query(50, ge=1, le=500),
    offset: int = Query(0, ge=0),
    db: Session = Depends(get_db),
):
    """List consent records with optional filters and pagination."""
    query = db.query(EinwilligungenConsentDB).filter(
        EinwilligungenConsentDB.tenant_id == tenant_id
    )

    if user_id:
        query = query.filter(EinwilligungenConsentDB.user_id == user_id)
    if data_point_id:
        query = query.filter(EinwilligungenConsentDB.data_point_id == data_point_id)
    if granted is not None:
        query = query.filter(EinwilligungenConsentDB.granted == granted)

    total = query.count()
    consents = query.order_by(EinwilligungenConsentDB.created_at.desc()).offset(offset).limit(limit).all()

    return {
        "total": total,
        "offset": offset,
        "limit": limit,
        "consents": [
            {
                "id": str(c.id),
                "tenant_id": c.tenant_id,
                "user_id": c.user_id,
                "data_point_id": c.data_point_id,
                "granted": c.granted,
                "granted_at": c.granted_at,
                "revoked_at": c.revoked_at,
                "consent_version": c.consent_version,
                "source": c.source,
                "ip_address": c.ip_address,
                "user_agent": c.user_agent,
                "created_at": c.created_at,
                "history": [
                    {
                        "id": str(h.id),
                        "action": h.action,
                        "consent_version": h.consent_version,
                        "ip_address": h.ip_address,
                        "user_agent": h.user_agent,
                        "source": h.source,
                        "created_at": h.created_at,
                    }
                    for h in db.query(EinwilligungenConsentHistoryDB)
                    .filter(EinwilligungenConsentHistoryDB.consent_id == c.id)
                    .order_by(EinwilligungenConsentHistoryDB.created_at.asc())
                    .all()
                ],
            }
            for c in consents
        ],
    }


@router.post("/consents", status_code=201)
async def create_consent(
    request: ConsentCreate,
    tenant_id: str = Depends(_get_tenant),
    db: Session = Depends(get_db),
):
    """Record a new consent entry."""
    consent = EinwilligungenConsentDB(
        tenant_id=tenant_id,
        user_id=request.user_id,
        data_point_id=request.data_point_id,
        granted=request.granted,
        granted_at=datetime.now(timezone.utc),
        consent_version=request.consent_version,
        source=request.source,
        ip_address=request.ip_address,
        user_agent=request.user_agent,
    )
    db.add(consent)
    _record_history(db, consent, 'granted')
    db.commit()
    db.refresh(consent)

    return {
        "success": True,
        "id": str(consent.id),
        "user_id": consent.user_id,
        "data_point_id": consent.data_point_id,
        "granted": consent.granted,
        "granted_at": consent.granted_at,
    }


@router.get("/consents/{consent_id}/history")
async def get_consent_history(
    consent_id: str,
    tenant_id: str = Depends(_get_tenant),
    db: Session = Depends(get_db),
):
    """Get the change history for a specific consent record."""
    entries = (
        db.query(EinwilligungenConsentHistoryDB)
        .filter(
            EinwilligungenConsentHistoryDB.consent_id == consent_id,
            EinwilligungenConsentHistoryDB.tenant_id == tenant_id,
        )
        .order_by(EinwilligungenConsentHistoryDB.created_at.asc())
        .all()
    )
    return [
        {
            "id": str(e.id),
            "consent_id": str(e.consent_id),
            "action": e.action,
            "consent_version": e.consent_version,
            "ip_address": e.ip_address,
            "user_agent": e.user_agent,
            "source": e.source,
            "created_at": e.created_at,
        }
        for e in entries
    ]


@router.put("/consents/{consent_id}/revoke")
async def revoke_consent(
    consent_id: str,
    tenant_id: str = Depends(_get_tenant),
    db: Session = Depends(get_db),
):
    """Revoke an active consent."""
    consent = db.query(EinwilligungenConsentDB).filter(
        EinwilligungenConsentDB.id == consent_id,
        EinwilligungenConsentDB.tenant_id == tenant_id,
    ).first()

    if not consent:
        raise HTTPException(status_code=404, detail=f"Consent {consent_id} not found")
    if consent.revoked_at:
        raise HTTPException(status_code=400, detail="Consent is already revoked")

    consent.revoked_at = datetime.now(timezone.utc)
    _record_history(db, consent, 'revoked')
    db.commit()
    db.refresh(consent)

    return {
        "success": True,
        "id": str(consent.id),
        "revoked_at": consent.revoked_at,
    }
