from pydantic import BaseModel, Field
from fastapi import APIRouter, Depends, Header, HTTPException, Request
from sqlalchemy import select, func
from sqlalchemy.orm import Session
from app.core.database import get_db
from app.api.deps import current_user
from app.models.entities import User,Tenant,TenantDomain,TenantRuntimeNamespace,TenantQuotaReservation,TenantRuntimeAudit
from app.services.saas_tenancy import resolve_membership
from app.services.tenant_runtime import ensure_namespace,register_domain,verify_domain,reserve_quota,finalize_quota,tenant_redis_key,tenant_object_key,tenant_queue
router=APIRouter(prefix='/api/v022',tags=['V022 Tenant Runtime Enforcement'])
PLATFORM={'admin','director'}
class DomainIn(BaseModel): hostname:str; is_primary:bool=False
class VerifyIn(BaseModel): token:str
class ReserveIn(BaseModel): metric_code:str; quantity:int=Field(gt=0); idempotency_key:str=Field(min_length=8,max_length=160); reference_type:str=''; reference_id:str=''; ttl_seconds:int=Field(default=900,ge=60,le=86400)
class FinalizeIn(BaseModel): consumed:bool=True

def tenant_admin(x_tenant_id:str=Header(...,alias='X-Tenant-ID'),db:Session=Depends(get_db),user:User=Depends(current_user)):
    tenant=db.get(Tenant,x_tenant_id)
    if not tenant or tenant.status!='active': raise HTTPException(404,'Tenant ativo não encontrado')
    role=getattr(user.role,'value',user.role); membership=resolve_membership(db,user,tenant.id)
    if role not in PLATFORM and (not membership or membership.tenant_role not in ('owner','admin')): raise HTTPException(403,'Administração do tenant necessária')
    return tenant
@router.get('/summary')
def summary(tenant:Tenant=Depends(tenant_admin),db:Session=Depends(get_db)):
    ns=ensure_namespace(db,tenant)
    return {'version':'V022','tenant_id':tenant.id,'isolation_mode':tenant.isolation_mode,
      'verified_domains':db.scalar(select(func.count(TenantDomain.id)).where(TenantDomain.tenant_id==tenant.id,TenantDomain.verified.is_(True))) or 0,
      'active_reservations':db.scalar(select(func.count(TenantQuotaReservation.id)).where(TenantQuotaReservation.tenant_id==tenant.id,TenantQuotaReservation.status=='reserved')) or 0,
      'namespace':ns}
@router.get('/runtime-context')
def runtime_context(request:Request,tenant:Tenant=Depends(tenant_admin),db:Session=Depends(get_db)):
    ns=ensure_namespace(db,tenant)
    return {'tenant_id':tenant.id,'redis_example':tenant_redis_key(ns,'cache:example'),'object_example':tenant_object_key(ns,'exports/example.parquet'),'queues':{p:tenant_queue(ns,p) for p in ('low','default','high')},'request_id':request.state.request_id}
@router.post('/domains')
def create_domain(p:DomainIn,tenant:Tenant=Depends(tenant_admin),db:Session=Depends(get_db)):
    try:return register_domain(db,tenant.id,p.hostname,p.is_primary)
    except ValueError as exc: raise HTTPException(409,str(exc))
@router.get('/domains')
def domains(tenant:Tenant=Depends(tenant_admin),db:Session=Depends(get_db)):
    return db.scalars(select(TenantDomain).where(TenantDomain.tenant_id==tenant.id).order_by(TenantDomain.created_at.desc())).all()
@router.post('/domains/{domain_id}/verify')
def verify(domain_id:str,p:VerifyIn,tenant:Tenant=Depends(tenant_admin),db:Session=Depends(get_db)):
    try:return verify_domain(db,tenant.id,domain_id,p.token)
    except ValueError as exc: raise HTTPException(400,str(exc))
@router.post('/quotas/reserve')
def reserve(p:ReserveIn,tenant:Tenant=Depends(tenant_admin),db:Session=Depends(get_db),user:User=Depends(current_user)):
    try:return reserve_quota(db,tenant.id,p.metric_code,p.quantity,p.idempotency_key,user.id,p.reference_type,p.reference_id,p.ttl_seconds)
    except PermissionError as exc: raise HTTPException(429,str(exc))
    except ValueError as exc: raise HTTPException(409,str(exc))
@router.post('/quotas/{reservation_id}/finalize')
def finalize(reservation_id:str,p:FinalizeIn,tenant:Tenant=Depends(tenant_admin),db:Session=Depends(get_db)):
    item=db.scalar(select(TenantQuotaReservation).where(TenantQuotaReservation.id==reservation_id,TenantQuotaReservation.tenant_id==tenant.id))
    if not item: raise HTTPException(404,'Reserva não encontrada')
    return finalize_quota(db,item,p.consumed)
@router.get('/quotas')
def quotas(tenant:Tenant=Depends(tenant_admin),db:Session=Depends(get_db)):
    return db.scalars(select(TenantQuotaReservation).where(TenantQuotaReservation.tenant_id==tenant.id).order_by(TenantQuotaReservation.created_at.desc()).limit(500)).all()
