from datetime import datetime, timezone
from fastapi import APIRouter, Depends, Header, HTTPException
from pydantic import BaseModel, Field
from sqlalchemy import func, select
from sqlalchemy.orm import Session
from app.api.deps import current_user
from app.core.database import get_db
from app.models.entities import (AIAgent, AIAgentMemoryV032, AIAgentVersionV032, AIBudgetPolicyV032,
    AIEvaluationRunV032, AIEvaluationSuiteV032, Tenant, User)
from app.services.ai_studio_v032 import approve_version, budget_status, create_agent_version, run_evaluation, validate_agent_definition
from app.services.saas_tenancy import resolve_membership
router=APIRouter(prefix='/api/v032',tags=['V032 AI Studio & Agent Builder'])
ADMIN={'admin','director','data_steward'}

def ctx(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); member=resolve_membership(db,user,x_tenant_id) if tenant else None
    if not tenant or tenant.status!='active': raise HTTPException(404,'Tenant ativo não encontrado')
    if getattr(user.role,'value',user.role) not in {'admin','director'} and not member: raise HTTPException(403,'Usuário não pertence ao tenant')
    return tenant,user

def admin(c=Depends(ctx)):
    if getattr(c[1].role,'value',c[1].role) not in ADMIN: raise HTTPException(403,'Perfil administrativo necessário')
    return c
class VersionIn(BaseModel): change_notes:str=''
class SuiteIn(BaseModel): name:str; description:str=''; cases:list=Field(default_factory=list); thresholds:dict=Field(default_factory=lambda:{'minimum_score':70})
class BudgetIn(BaseModel): subject_type:str='tenant'; subject_value:str='*'; monthly_budget_micros:int=Field(ge=0); warning_percent:int=Field(default=80,ge=1,le=100); hard_block:bool=True; allowed_model_ids:list=Field(default_factory=list); active:bool=True
class MemoryIn(BaseModel): agent_id:str; memory_type:str='preference'; content:str=Field(min_length=1,max_length=5000); metadata_json:dict=Field(default_factory=dict); expires_at:datetime|None=None
class ValidateIn(BaseModel): definition:dict

@router.get('/summary')
def summary(c=Depends(ctx),db:Session=Depends(get_db)):
    t,_=c
    return {'version':'V032','versions':db.scalar(select(func.count()).select_from(AIAgentVersionV032).where(AIAgentVersionV032.tenant_id==t.id)) or 0,'suites':db.scalar(select(func.count()).select_from(AIEvaluationSuiteV032).where(AIEvaluationSuiteV032.tenant_id==t.id)) or 0,'budgets':db.scalar(select(func.count()).select_from(AIBudgetPolicyV032).where(AIBudgetPolicyV032.tenant_id==t.id)) or 0}
@router.post('/agents/validate')
def validate(p:ValidateIn,c=Depends(admin)): return {'valid':not (errors:=validate_agent_definition(p.definition)),'errors':errors}
@router.post('/agents/{agent_id}/versions',status_code=201)
def version(agent_id:str,p:VersionIn,c=Depends(admin),db:Session=Depends(get_db)):
    t,u=c; agent=db.get(AIAgent,agent_id)
    if not agent or agent.tenant_id!=t.id: raise HTTPException(404,'Agente não encontrado')
    try: return create_agent_version(db,t.id,agent,u.id,p.change_notes)
    except ValueError as e: raise HTTPException(400,str(e))
@router.get('/agents/{agent_id}/versions')
def versions(agent_id:str,c=Depends(ctx),db:Session=Depends(get_db)):
    t,_=c; return db.scalars(select(AIAgentVersionV032).where(AIAgentVersionV032.tenant_id==t.id,AIAgentVersionV032.agent_id==agent_id).order_by(AIAgentVersionV032.version.desc())).all()
@router.post('/versions/{version_id}/approve')
def approve(version_id:str,c=Depends(admin),db:Session=Depends(get_db)):
    t,u=c; item=db.get(AIAgentVersionV032,version_id)
    if not item or item.tenant_id!=t.id: raise HTTPException(404,'Versão não encontrada')
    return approve_version(db,item,u.id)
@router.post('/evaluation-suites',status_code=201)
def create_suite(p:SuiteIn,c=Depends(admin),db:Session=Depends(get_db)):
    t,u=c; item=AIEvaluationSuiteV032(tenant_id=t.id,created_by=u.id,**p.model_dump()); db.add(item); db.commit(); db.refresh(item); return item
@router.get('/evaluation-suites')
def suites(c=Depends(ctx),db:Session=Depends(get_db)):
    t,_=c; return db.scalars(select(AIEvaluationSuiteV032).where(AIEvaluationSuiteV032.tenant_id==t.id,AIEvaluationSuiteV032.active.is_(True))).all()
@router.post('/evaluation-suites/{suite_id}/run/{agent_id}')
def evaluate(suite_id:str,agent_id:str,version_id:str|None=None,c=Depends(admin),db:Session=Depends(get_db)):
    t,u=c; suite=db.get(AIEvaluationSuiteV032,suite_id); agent=db.get(AIAgent,agent_id)
    if not suite or suite.tenant_id!=t.id or not agent or agent.tenant_id!=t.id: raise HTTPException(404,'Suíte ou agente não encontrado')
    return run_evaluation(db,t.id,suite,agent,u.id,version_id)
@router.get('/evaluation-runs')
def runs(c=Depends(ctx),db:Session=Depends(get_db)):
    t,_=c; return db.scalars(select(AIEvaluationRunV032).where(AIEvaluationRunV032.tenant_id==t.id).order_by(AIEvaluationRunV032.started_at.desc()).limit(200)).all()
@router.put('/budgets')
def budget(p:BudgetIn,c=Depends(admin),db:Session=Depends(get_db)):
    t,u=c; item=db.scalar(select(AIBudgetPolicyV032).where(AIBudgetPolicyV032.tenant_id==t.id,AIBudgetPolicyV032.subject_type==p.subject_type,AIBudgetPolicyV032.subject_value==p.subject_value))
    if not item: item=AIBudgetPolicyV032(tenant_id=t.id,created_by=u.id,**p.model_dump()); db.add(item)
    else:
        for k,v in p.model_dump().items(): setattr(item,k,v)
    db.commit(); db.refresh(item); return item
@router.get('/budgets')
def budgets(c=Depends(ctx),db:Session=Depends(get_db)):
    t,_=c; items=db.scalars(select(AIBudgetPolicyV032).where(AIBudgetPolicyV032.tenant_id==t.id,AIBudgetPolicyV032.active.is_(True))).all(); return [{'policy':x,'status':budget_status(db,t.id,x)} for x in items]
@router.post('/memory',status_code=201)
def memory(p:MemoryIn,c=Depends(ctx),db:Session=Depends(get_db)):
    t,u=c; agent=db.get(AIAgent,p.agent_id)
    if not agent or agent.tenant_id!=t.id: raise HTTPException(404,'Agente não encontrado')
    item=AIAgentMemoryV032(tenant_id=t.id,user_id=u.id,**p.model_dump()); db.add(item); db.commit(); db.refresh(item); return item
@router.get('/memory/{agent_id}')
def memory_list(agent_id:str,c=Depends(ctx),db:Session=Depends(get_db)):
    t,u=c; now=datetime.now(timezone.utc); q=select(AIAgentMemoryV032).where(AIAgentMemoryV032.tenant_id==t.id,AIAgentMemoryV032.agent_id==agent_id,AIAgentMemoryV032.user_id==u.id).order_by(AIAgentMemoryV032.created_at.desc()); return [x for x in db.scalars(q).all() if not x.expires_at or x.expires_at>now]
