"""
FastAPI routes for Loeschfristen (Retention Policies).

Endpoints:
  GET    /loeschfristen              — list (filter: status, retention_driver, search; limit/offset)
  GET    /loeschfristen/stats        — total, active, draft, review_needed, archived, legal_holds_count
  POST   /loeschfristen              — create
  GET    /loeschfristen/{id}         — get single
  PUT    /loeschfristen/{id}         — full update
  PUT    /loeschfristen/{id}/status  — quick status update
  DELETE /loeschfristen/{id}         — delete (204)
"""

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

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

from classroom_engine.database import get_db
from .tenant_utils import get_tenant_id as _get_tenant_id
from .db_utils import row_to_dict as _row_to_dict

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


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

class LoeschfristCreate(BaseModel):
    policy_id: Optional[str] = None
    data_object_name: str
    description: Optional[str] = None
    affected_groups: Optional[List[Any]] = None
    data_categories: Optional[List[Any]] = None
    primary_purpose: Optional[str] = None
    deletion_trigger: str = "PURPOSE_END"
    retention_driver: Optional[str] = None
    retention_driver_detail: Optional[str] = None
    retention_duration: Optional[int] = None
    retention_unit: Optional[str] = None
    retention_description: Optional[str] = None
    start_event: Optional[str] = None
    has_active_legal_hold: bool = False
    legal_holds: Optional[List[Any]] = None
    storage_locations: Optional[List[Any]] = None
    deletion_method: Optional[str] = None
    deletion_method_detail: Optional[str] = None
    responsible_role: Optional[str] = None
    responsible_person: Optional[str] = None
    release_process: Optional[str] = None
    linked_vvt_activity_ids: Optional[List[Any]] = None
    linked_vendor_ids: Optional[List[Any]] = None
    status: str = "DRAFT"
    last_review_date: Optional[datetime] = None
    next_review_date: Optional[datetime] = None
    review_interval: Optional[str] = None
    tags: Optional[List[Any]] = None


class LoeschfristUpdate(BaseModel):
    policy_id: Optional[str] = None
    data_object_name: Optional[str] = None
    description: Optional[str] = None
    affected_groups: Optional[List[Any]] = None
    data_categories: Optional[List[Any]] = None
    primary_purpose: Optional[str] = None
    deletion_trigger: Optional[str] = None
    retention_driver: Optional[str] = None
    retention_driver_detail: Optional[str] = None
    retention_duration: Optional[int] = None
    retention_unit: Optional[str] = None
    retention_description: Optional[str] = None
    start_event: Optional[str] = None
    has_active_legal_hold: Optional[bool] = None
    legal_holds: Optional[List[Any]] = None
    storage_locations: Optional[List[Any]] = None
    deletion_method: Optional[str] = None
    deletion_method_detail: Optional[str] = None
    responsible_role: Optional[str] = None
    responsible_person: Optional[str] = None
    release_process: Optional[str] = None
    linked_vvt_activity_ids: Optional[List[Any]] = None
    linked_vendor_ids: Optional[List[Any]] = None
    status: Optional[str] = None
    last_review_date: Optional[datetime] = None
    next_review_date: Optional[datetime] = None
    review_interval: Optional[str] = None
    tags: Optional[List[Any]] = None


class StatusUpdate(BaseModel):
    status: str


# JSONB fields that need CAST
JSONB_FIELDS = {
    "affected_groups", "data_categories", "legal_holds",
    "storage_locations", "linked_vvt_activity_ids", "linked_vendor_ids", "tags"
}


# =============================================================================
# Routes
# =============================================================================

@router.get("")
async def list_loeschfristen(
    status: Optional[str] = Query(None),
    retention_driver: Optional[str] = Query(None),
    search: Optional[str] = Query(None),
    limit: int = Query(500, ge=1, le=1000),
    offset: int = Query(0, ge=0),
    db: Session = Depends(get_db),
    tenant_id: str = Depends(_get_tenant_id),
):
    """List Loeschfristen with optional filters."""

    where_clauses = ["tenant_id = :tenant_id"]
    params: Dict[str, Any] = {"tenant_id": tenant_id, "limit": limit, "offset": offset}

    if status:
        where_clauses.append("status = :status")
        params["status"] = status
    if retention_driver:
        where_clauses.append("retention_driver = :retention_driver")
        params["retention_driver"] = retention_driver
    if search:
        where_clauses.append("(data_object_name ILIKE :search OR description ILIKE :search OR policy_id ILIKE :search)")
        params["search"] = f"%{search}%"

    where_sql = " AND ".join(where_clauses)

    total_row = db.execute(
        text(f"SELECT COUNT(*) FROM compliance_loeschfristen WHERE {where_sql}"),
        params,
    ).fetchone()
    total = total_row[0] if total_row else 0

    rows = db.execute(
        text(f"""
            SELECT * FROM compliance_loeschfristen
            WHERE {where_sql}
            ORDER BY
                CASE status
                    WHEN 'ACTIVE' THEN 0
                    WHEN 'REVIEW_NEEDED' THEN 1
                    WHEN 'DRAFT' THEN 2
                    ELSE 3
                END,
                created_at DESC
            LIMIT :limit OFFSET :offset
        """),
        params,
    ).fetchall()

    return {
        "policies": [_row_to_dict(r) for r in rows],
        "total": total,
    }


@router.get("/stats")
async def get_loeschfristen_stats(
    db: Session = Depends(get_db),
    tenant_id: str = Depends(_get_tenant_id),
):
    """Return Loeschfristen statistics."""

    row = db.execute(text("""
        SELECT
            COUNT(*)                                              AS total,
            COUNT(*) FILTER (WHERE status = 'ACTIVE')             AS active,
            COUNT(*) FILTER (WHERE status = 'DRAFT')              AS draft,
            COUNT(*) FILTER (WHERE status = 'REVIEW_NEEDED')      AS review_needed,
            COUNT(*) FILTER (WHERE status = 'ARCHIVED')           AS archived,
            COUNT(*) FILTER (WHERE has_active_legal_hold = TRUE)  AS legal_holds_count,
            COUNT(*) FILTER (
                WHERE next_review_date IS NOT NULL
                  AND next_review_date < NOW()
                  AND status NOT IN ('ARCHIVED')
            )                                                      AS overdue_reviews
        FROM compliance_loeschfristen
        WHERE tenant_id = :tenant_id
    """), {"tenant_id": tenant_id}).fetchone()

    if row:
        d = dict(row._mapping)
        return {k: int(v or 0) for k, v in d.items()}
    return {"total": 0, "active": 0, "draft": 0, "review_needed": 0,
            "archived": 0, "legal_holds_count": 0, "overdue_reviews": 0}


@router.post("", status_code=201)
async def create_loeschfrist(
    payload: LoeschfristCreate,
    db: Session = Depends(get_db),
    tenant_id: str = Depends(_get_tenant_id),
):
    """Create a new Loeschfrist policy."""

    data = payload.model_dump()

    # Build INSERT with JSONB casts
    columns = ["tenant_id"] + list(data.keys())
    value_parts = [":tenant_id"]
    params: Dict[str, Any] = {"tenant_id": tenant_id}

    for k, v in data.items():
        if k in JSONB_FIELDS:
            value_parts.append(f"CAST(:{k} AS jsonb)")
            params[k] = json.dumps(v if v is not None else [])
        else:
            value_parts.append(f":{k}")
            params[k] = v

    cols_sql = ", ".join(columns)
    vals_sql = ", ".join(value_parts)

    row = db.execute(
        text(f"INSERT INTO compliance_loeschfristen ({cols_sql}) VALUES ({vals_sql}) RETURNING *"),
        params,
    ).fetchone()
    db.commit()
    return _row_to_dict(row)


@router.get("/{policy_id}")
async def get_loeschfrist(
    policy_id: str,
    db: Session = Depends(get_db),
    tenant_id: str = Depends(_get_tenant_id),
):
    row = db.execute(
        text("SELECT * FROM compliance_loeschfristen WHERE id = :id AND tenant_id = :tenant_id"),
        {"id": policy_id, "tenant_id": tenant_id},
    ).fetchone()
    if not row:
        raise HTTPException(status_code=404, detail="Loeschfrist not found")
    return _row_to_dict(row)


@router.put("/{policy_id}")
async def update_loeschfrist(
    policy_id: str,
    payload: LoeschfristUpdate,
    db: Session = Depends(get_db),
    tenant_id: str = Depends(_get_tenant_id),
):
    """Full update of a Loeschfrist policy."""

    updates: Dict[str, Any] = {"id": policy_id, "tenant_id": tenant_id, "updated_at": datetime.now(timezone.utc)}
    set_clauses = ["updated_at = :updated_at"]

    for field, value in payload.model_dump(exclude_unset=True).items():
        if field in JSONB_FIELDS:
            updates[field] = json.dumps(value if value is not None else [])
            set_clauses.append(f"{field} = CAST(:{field} AS jsonb)")
        else:
            updates[field] = value
            set_clauses.append(f"{field} = :{field}")

    if len(set_clauses) == 1:
        raise HTTPException(status_code=400, detail="No fields to update")

    row = db.execute(
        text(f"""
            UPDATE compliance_loeschfristen
            SET {', '.join(set_clauses)}
            WHERE id = :id AND tenant_id = :tenant_id
            RETURNING *
        """),
        updates,
    ).fetchone()
    db.commit()

    if not row:
        raise HTTPException(status_code=404, detail="Loeschfrist not found")
    return _row_to_dict(row)


@router.put("/{policy_id}/status")
async def update_loeschfrist_status(
    policy_id: str,
    payload: StatusUpdate,
    db: Session = Depends(get_db),
    tenant_id: str = Depends(_get_tenant_id),
):
    """Quick status update."""
    valid = {"DRAFT", "ACTIVE", "REVIEW_NEEDED", "ARCHIVED"}
    if payload.status not in valid:
        raise HTTPException(status_code=400, detail=f"Invalid status. Must be one of: {', '.join(valid)}")

    row = db.execute(
        text("""
            UPDATE compliance_loeschfristen
            SET status = :status, updated_at = :now
            WHERE id = :id AND tenant_id = :tenant_id
            RETURNING *
        """),
        {"status": payload.status, "now": datetime.now(timezone.utc), "id": policy_id, "tenant_id": tenant_id},
    ).fetchone()
    db.commit()

    if not row:
        raise HTTPException(status_code=404, detail="Loeschfrist not found")
    return _row_to_dict(row)


@router.delete("/{policy_id}", status_code=204)
async def delete_loeschfrist(
    policy_id: str,
    db: Session = Depends(get_db),
    tenant_id: str = Depends(_get_tenant_id),
):
    result = db.execute(
        text("DELETE FROM compliance_loeschfristen WHERE id = :id AND tenant_id = :tenant_id"),
        {"id": policy_id, "tenant_id": tenant_id},
    )
    db.commit()
    if result.rowcount == 0:
        raise HTTPException(status_code=404, detail="Loeschfrist not found")


# =============================================================================
# Versioning
# =============================================================================

@router.get("/{policy_id}/versions")
async def list_loeschfristen_versions(
    policy_id: str,
    db: Session = Depends(get_db),
    tenant_id: str = Depends(_get_tenant_id),
):
    """List all versions for a Loeschfrist."""
    from .versioning_utils import list_versions
    return list_versions(db, "loeschfristen", policy_id, tenant_id)


@router.get("/{policy_id}/versions/{version_number}")
async def get_loeschfristen_version(
    policy_id: str,
    version_number: int,
    db: Session = Depends(get_db),
    tenant_id: str = Depends(_get_tenant_id),
):
    """Get a specific Loeschfristen version with full snapshot."""
    from .versioning_utils import get_version
    v = get_version(db, "loeschfristen", policy_id, version_number, tenant_id)
    if not v:
        raise HTTPException(status_code=404, detail=f"Version {version_number} not found")
    return v
