"""Licence management endpoints."""

from datetime import datetime, timezone
from secrets import token_urlsafe
from typing import Dict, List, Optional

import csv
import io

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

from ..database import get_session
from ..models import AdminAudit, License
from ..sast_defaults import resolve_license_limits, resolve_sast_settings
from ..security import require_roles
from ..utils import log_action

router = APIRouter()


class LicenseOut(BaseModel):
    email: str
    api_key: Optional[str] = None
    tier: str
    status: str
    max_domains: Optional[int] = 0
    max_users: Optional[int] = 0
    allow_source_scan: Optional[bool] = False
    max_source_files: Optional[int] = 0
    max_source_size_mb: Optional[int] = 0
    advanced_rules: Optional[bool] = False
    stripe_customer_id: Optional[str] = None
    stripe_subscription_id: Optional[str] = None
    created_at: Optional[datetime] = None
    updated_at: Optional[datetime] = None
    expires_at: Optional[datetime] = None

    model_config = ConfigDict(from_attributes=True)


class LicenseCreate(BaseModel):
    email: str
    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 LicenseUpdate(BaseModel):
    tier: Optional[str] = None
    status: Optional[str] = None
    max_domains: Optional[int] = None
    max_users: Optional[int] = None
    allow_source_scan: Optional[bool] = None
    max_source_files: Optional[int] = None
    max_source_size_mb: Optional[int] = None
    advanced_rules: Optional[bool] = None
    expires_at: Optional[datetime] = None
    stripe_customer_id: Optional[str] = None
    stripe_subscription_id: Optional[str] = None


class LicenseListResponse(BaseModel):
    items: List[LicenseOut]
    total: int


def _apply_filters(query, status: Optional[str], email: 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 email:
        query = query.filter(func.lower(License.email) == email.lower())
    if search:
        pattern = f"%{search.lower()}%"
        query = query.filter(
            func.lower(License.email).like(pattern) | func.lower(License.api_key).like(pattern)
        )
    return query


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


@router.get("/", response_model=LicenseListResponse)
def list_licenses(
    limit: int = Query(100, ge=1, le=500),
    offset: int = Query(0, ge=0),
    status: Optional[str] = Query(None, description="Filtre exact sur le statut"),
    email: Optional[str] = Query(None, description="Filtre exact sur l'email"),
    tier: Optional[str] = Query(None, description="Filtre exact sur le tier"),
    search: Optional[str] = Query(None, description="Recherche partielle sur email/api_key"),
    session=Depends(get_session),
    _admin=Depends(require_roles("super-admin", "analyst")),
):
    query = session.query(License)
    query = _apply_filters(query, status=status, email=email, tier=tier, search=search)
    total = query.count()
    records = (
        query.order_by(License.created_at.desc())
        .offset(offset)
        .limit(limit)
        .all()
    )
    return LicenseListResponse(items=records, total=total)


@router.get("/export")
def export_licenses(
    format: str = Query("json", pattern="^(json|csv)$"),
    status: Optional[str] = Query(None),
    email: 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_filters(query, status=status, email=email, tier=tier, search=search)
    records = query.order_by(License.created_at.desc()).all()
    payload = [
        {
            "email": licence.email,
            "api_key": licence.api_key,
            "tier": licence.tier,
            "status": licence.status,
            "max_domains": licence.max_domains,
            "max_users": licence.max_users,
            "allow_source_scan": licence.allow_source_scan,
            "max_source_files": licence.max_source_files,
            "max_source_size_mb": licence.max_source_size_mb,
            "advanced_rules": licence.advanced_rules,
            "stripe_customer_id": licence.stripe_customer_id,
            "stripe_subscription_id": licence.stripe_subscription_id,
            "created_at": licence.created_at.isoformat() if licence.created_at else None,
            "updated_at": licence.updated_at.isoformat() if licence.updated_at else None,
            "expires_at": licence.expires_at.isoformat() if licence.expires_at else None,
        }
        for licence in records
    ]

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

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

    buffer = io.StringIO()
    writer = csv.DictWriter(
        buffer,
        fieldnames=[
            "email",
            "api_key",
            "tier",
            "status",
            "max_domains",
            "max_users",
            "allow_source_scan",
            "max_source_files",
            "max_source_size_mb",
            "advanced_rules",
            "stripe_customer_id",
            "stripe_subscription_id",
            "created_at",
            "updated_at",
            "expires_at",
        ],
    )
    writer.writeheader()
    for row in payload:
        writer.writerow(row)
    return {
        "format": "csv",
        "generated_at": datetime.now(timezone.utc).isoformat(),
        "content": buffer.getvalue(),
        "count": len(payload),
    }


@router.get("/{api_key}", response_model=LicenseOut)
def get_license(
    api_key: str,
    session=Depends(get_session),
    _admin=Depends(require_roles("super-admin", "analyst")),
):
    licence = session.query(License).filter(License.api_key == api_key).first()
    if not licence:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Licence introuvable")
    return licence


@router.post("/", response_model=LicenseOut, status_code=status.HTTP_201_CREATED)
def create_license(
    payload: LicenseCreate,
    session=Depends(get_session),
    _admin=Depends(require_roles("super-admin")),
):
    api_key = _generate_api_key()
    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=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": "admin_created", "created_at": now.isoformat()}],
    )
    session.add(licence)
    log_action(session, _admin.email, "license_created", target=licence.api_key, metadata=payload.model_dump())
    session.commit()
    session.refresh(licence)
    return licence


@router.patch("/{api_key}", response_model=LicenseOut)
def update_license(
    api_key: str,
    payload: LicenseUpdate,
    session=Depends(get_session),
    _admin=Depends(require_roles("super-admin")),
):
    licence = session.query(License).filter(License.api_key == api_key).first()
    if not licence:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Licence introuvable")

    if payload.tier is not None:
        licence.tier = payload.tier.upper()
    if payload.status is not None:
        licence.status = payload.status.lower()
    if payload.max_domains is not None:
        licence.max_domains = payload.max_domains
    if payload.max_users is not None:
        licence.max_users = payload.max_users
    if payload.allow_source_scan is not None:
        licence.allow_source_scan = payload.allow_source_scan
    if payload.max_source_files is not None:
        licence.max_source_files = payload.max_source_files
    if payload.max_source_size_mb is not None:
        licence.max_source_size_mb = payload.max_source_size_mb
    if payload.advanced_rules is not None:
        licence.advanced_rules = payload.advanced_rules
    if payload.expires_at is not None:
        licence.expires_at = payload.expires_at
    if payload.stripe_customer_id is not None:
        licence.stripe_customer_id = payload.stripe_customer_id
    if payload.stripe_subscription_id is not None:
        licence.stripe_subscription_id = payload.stripe_subscription_id

    licence.updated_at = datetime.now(timezone.utc)
    events = licence.events or []
    events.append({"event_type": "admin_updated", "created_at": licence.updated_at.isoformat()})
    licence.events = events

    session.add(licence)
    log_action(session, _admin.email, "license_updated", target=api_key, metadata=payload.model_dump(exclude_none=True))
    session.commit()
    session.refresh(licence)
    return licence


@router.get("/{api_key}/history")
def license_history(api_key: str, session=Depends(get_session), _admin=Depends(require_roles("super-admin", "analyst"))):
    records = (
        session.query(AdminAudit)
        .filter(AdminAudit.target == api_key)
        .order_by(AdminAudit.created_at.desc())
        .all()
    )
    return [
        {
            "actor": rec.actor_email,
            "action": rec.action,
            "details": rec.details,
            "created_at": rec.created_at.isoformat() if rec.created_at else None,
        }
        for rec in records
    ]


@router.post("/batch/status")
def batch_update_status(
    payload: Dict[str, str],
    session=Depends(get_session),
    _admin=Depends(require_roles("super-admin")),
):
    status = payload.get("status")
    api_keys = payload.get("api_keys", [])
    if not status or not api_keys:
        raise HTTPException(status_code=400, detail="status et api_keys requis")
    updated = session.query(License).filter(License.api_key.in_(api_keys)).update(
        {"status": status.lower()}, synchronize_session=False
    )
    log_action(session, _admin.email, "license_batch_status", metadata={"status": status, "count": updated})
    session.commit()
    return {"updated": updated}


@router.delete("/{api_key}", status_code=status.HTTP_204_NO_CONTENT)
def delete_license(
    api_key: str,
    session=Depends(get_session),
    _admin=Depends(require_roles("super-admin")),
):
    """
    Suppression logique d'une licence : au lieu de supprimer complètement,
    on remet le compte en tier FREE pour conserver l'historique du client.
    """
    licence = session.query(License).filter(License.api_key == api_key).first()
    if not licence:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Licence introuvable")

    # Sauvegarder l'ancien tier pour l'audit
    old_tier = licence.tier
    
    # Réinitialiser en FREE au lieu de supprimer
    licence.tier = "FREE"
    licence.status = "active"
    free_limits = resolve_license_limits(session, "FREE")
    licence.max_domains = free_limits["max_domains"]
    licence.max_users = free_limits["max_users"]
    free_sast = resolve_sast_settings(session, "FREE")
    licence.allow_source_scan = free_sast["allow_source_scan"]
    licence.max_source_files = free_sast["max_source_files"]
    licence.max_source_size_mb = free_sast["max_source_size_mb"]
    licence.advanced_rules = free_sast["advanced_rules"]
    licence.stripe_customer_id = None
    licence.stripe_subscription_id = None
    licence.expires_at = None
    licence.updated_at = datetime.now(timezone.utc)
    
    # Ajouter événement dans l'historique
    events = licence.events or []
    events.append({
        "event_type": "license_downgraded_to_free",
        "old_tier": old_tier,
        "created_at": licence.updated_at.isoformat(),
        "reason": "admin_deleted"
    })
    licence.events = events
    
    session.add(licence)
    log_action(
        session, 
        _admin.email, 
        "license_downgraded_to_free", 
        target=api_key, 
        metadata={"old_tier": old_tier, "reason": "admin_deleted"}
    )
    session.commit()
    return None


@router.delete("/{api_key}/hard", status_code=status.HTTP_204_NO_CONTENT)
def hard_delete_license(
    api_key: str,
    session=Depends(get_session),
    _admin=Depends(require_roles("super-admin")),
):
    """
    Suppression définitive (hard delete) d'une licence.
    ⚠️ ATTENTION : Cette action est irréversible et supprime toutes les données.
    À utiliser uniquement pour nettoyer des comptes de test ou en cas d'erreur grave.
    """
    licence = session.query(License).filter(License.api_key == api_key).first()
    if not licence:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Licence introuvable")

    email = licence.email
    tier = licence.tier
    
    # Suppression définitive
    session.delete(licence)
    log_action(
        session, 
        _admin.email, 
        "license_hard_deleted", 
        target=api_key, 
        metadata={"email": email, "tier": tier, "warning": "irreversible"}
    )
    session.commit()
    return None
