"""Client management endpoints."""

from __future__ import annotations

import csv
import io
from collections import defaultdict
from datetime import datetime, timezone
from secrets import token_urlsafe
from typing import Dict, List, Optional

from fastapi import APIRouter, Depends, HTTPException, Query, status
from pydantic import BaseModel, ConfigDict, EmailStr
from sqlalchemy import func

from ..database import get_session
from ..models.license import License
from ..sast_defaults import resolve_license_limits, resolve_sast_settings
from ..security import require_roles
from ..utils import log_action
from .licenses import LicenseOut

router = APIRouter()


class ClientSummary(BaseModel):
    email: str
    tier: str
    status: str
    licenses: int
    active_licenses: int
    last_license_created_at: Optional[datetime] = None

    model_config = ConfigDict(from_attributes=True)


class ClientListResponse(BaseModel):
    items: List[ClientSummary]
    total: int


class ClientCreate(BaseModel):
    email: EmailStr
    tier: str = "FREE"
    status: str = "active"
    max_domains: int = 10
    max_users: int = 1
    allow_source_scan: Optional[bool] = None
    max_source_files: Optional[int] = None
    max_source_size_mb: Optional[int] = None
    advanced_rules: Optional[bool] = None
    stripe_customer_id: Optional[str] = None
    stripe_subscription_id: Optional[str] = None
    expires_at: Optional[datetime] = None


class ClientInvitation(BaseModel):
    email: EmailStr


class ClientDetail(BaseModel):
    email: str
    tier: str
    status: str
    licenses: List[LicenseOut]
    total_licenses: int


def _generate_api_key() -> str:
    return f"ws_live_{token_urlsafe(32)}"


def _apply_client_filters(query, status: Optional[str], tier: Optional[str], search: Optional[str]):
    if status:
        query = query.filter(License.status == status.lower())
    if tier:
        query = query.filter(License.tier == tier.upper())
    if search:
        pattern = f"%{search.lower()}%"
        query = query.filter(func.lower(License.email).like(pattern))
    return query


@router.get("/", response_model=ClientListResponse)
def list_clients(
    limit: int = Query(50, ge=1, le=500),
    offset: int = Query(0, ge=0),
    status: Optional[str] = Query(None),
    tier: Optional[str] = Query(None),
    search: Optional[str] = Query(None),
    session=Depends(get_session),
    _admin=Depends(require_roles("super-admin", "analyst")),
):
    query = session.query(License)
    query = _apply_client_filters(query, status=status, tier=tier, search=search)

    summary: Dict[str, Dict[str, object]] = defaultdict(
        lambda: {
            "tier": "FREE",
            "status": "unknown",
            "licenses": 0,
            "active_licenses": 0,
            "last_license_created_at": None,
        },
    )
    for licence in query.all():
        bucket = summary[licence.email]
        bucket["licenses"] = int(bucket["licenses"]) + 1
        if licence.status == "active":
            bucket["active_licenses"] = int(bucket["active_licenses"]) + 1
        bucket["tier"] = licence.tier
        bucket["status"] = licence.status
        created_at = licence.created_at
        if created_at:
            previous = bucket["last_license_created_at"]
            if previous is None or created_at > previous:
                bucket["last_license_created_at"] = created_at

    items = [
        ClientSummary(
            email=email,
            tier=data["tier"],
            status=data["status"],
            licenses=data["licenses"],
            active_licenses=data["active_licenses"],
            last_license_created_at=data["last_license_created_at"],
        )
        for email, data in summary.items()
    ]
    items.sort(key=lambda item: item.email)
    total = len(items)
    sliced = items[offset : offset + limit]
    return ClientListResponse(items=sliced, total=total)


@router.get("/export")
def export_clients(
    format: str = Query("json", pattern="^(json|csv)$"),
    status: Optional[str] = Query(None),
    tier: Optional[str] = Query(None),
    search: Optional[str] = Query(None),
    session=Depends(get_session),
    _admin=Depends(require_roles("super-admin", "analyst")),
):
    query = session.query(License)
    query = _apply_client_filters(query, status=status, tier=tier, search=search)

    aggregates: Dict[str, Dict[str, object]] = defaultdict(
        lambda: {"tier": "FREE", "status": "unknown", "licenses": 0, "active_licenses": 0},
    )
    for licence in query.all():
        bucket = aggregates[licence.email]
        bucket["licenses"] = int(bucket["licenses"]) + 1
        if licence.status == "active":
            bucket["active_licenses"] = int(bucket["active_licenses"]) + 1
        bucket["tier"] = licence.tier
        bucket["status"] = licence.status

    items = [
        {
            "email": email,
            "tier": data["tier"],
            "status": data["status"],
            "licenses": data["licenses"],
            "active_licenses": data["active_licenses"],
        }
        for email, data in aggregates.items()
    ]
    items.sort(key=lambda item: item["email"])

    log_action(
        session,
        _admin.email,
        "clients_export",
        metadata={
            "format": format,
            "count": len(items),
            "filters": {"status": status, "tier": tier, "search": search},
        },
    )
    session.commit()

    if format == "json":
        return {
            "format": "json",
            "generated_at": datetime.now(timezone.utc).isoformat(),
            "count": len(items),
            "items": items,
        }

    buffer = io.StringIO()
    writer = csv.DictWriter(buffer, fieldnames=["email", "tier", "status", "licenses", "active_licenses"])
    writer.writeheader()
    for row in items:
        writer.writerow(row)
    return {
        "format": "csv",
        "generated_at": datetime.now(timezone.utc).isoformat(),
        "content": buffer.getvalue(),
        "count": len(items),
    }


@router.post("/", response_model=LicenseOut, status_code=status.HTTP_201_CREATED)
def create_client(
    payload: ClientCreate,
    session=Depends(get_session),
    _admin=Depends(require_roles("super-admin")),
):
    exists = session.query(License).filter(func.lower(License.email) == payload.email.lower()).first()
    if exists:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Un client existe deja avec cet email")

    now = datetime.now(timezone.utc)
    tier = payload.tier.upper()
    limits = resolve_license_limits(session, tier, payload.max_domains, payload.max_users)
    sast_settings = resolve_sast_settings(
        session,
        tier,
        allow_source_scan=payload.allow_source_scan,
        max_source_files=payload.max_source_files,
        max_source_size_mb=payload.max_source_size_mb,
        advanced_rules=payload.advanced_rules,
    )
    licence = License(
        email=payload.email,
        api_key=_generate_api_key(),
        tier=tier,
        status=payload.status.lower(),
        max_domains=limits["max_domains"],
        max_users=limits["max_users"],
        **sast_settings,
        stripe_customer_id=payload.stripe_customer_id,
        stripe_subscription_id=payload.stripe_subscription_id,
        expires_at=payload.expires_at,
        created_at=now,
        updated_at=now,
        events=[{"event_type": "client_created", "created_at": now.isoformat()}],
    )
    session.add(licence)
    log_action(session, _admin.email, "client_created", target=licence.email, metadata=payload.model_dump())
    session.commit()
    session.refresh(licence)
    return licence


@router.get("/{email}", response_model=ClientDetail)
def get_client_detail(
    email: str,
    session=Depends(get_session),
    _admin=Depends(require_roles("super-admin", "analyst")),
):
    licences = (
        session.query(License)
        .filter(func.lower(License.email) == email.lower())
        .order_by(License.created_at.desc())
        .all()
    )
    if not licences:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Client introuvable")

    latest = licences[0]
    return ClientDetail(
        email=email,
        tier=latest.tier,
        status=latest.status,
        licenses=[LicenseOut.model_validate(licence) for licence in licences],
        total_licenses=len(licences),
    )


@router.post("/invitations", status_code=status.HTTP_202_ACCEPTED)
def create_invitation(
    payload: ClientInvitation,
    session=Depends(get_session),
    _admin=Depends(require_roles("super-admin")),
):
    log_action(session, _admin.email, "invitation_created", target=payload.email)
    session.commit()
    return {"message": "Invitation enregistree", "email": payload.email}


@router.post("/{email}/reset-api-keys")
def reset_api_keys(email: str, session=Depends(get_session), _admin=Depends(require_roles("super-admin"))):
    licences = session.query(License).filter(func.lower(License.email) == email.lower()).all()
    if not licences:
        raise HTTPException(status_code=404, detail="Client introuvable")

    for licence in licences:
        licence.api_key = f"ws_live_{email.replace('@', '_')}_{licence.id}_{token_urlsafe(6)}"
        licence.updated_at = datetime.now(timezone.utc)
        session.add(licence)
    log_action(session, _admin.email, "client_api_reset", target=email, metadata={"count": len(licences)})
    session.commit()
    return {"message": "Cles regenerees", "count": len(licences)}
