from fastapi import APIRouter,Depends,HTTPException
from pydantic import BaseModel,Field
from sqlalchemy import select
from sqlalchemy.orm import Session
from app.api.deps import get_db,current_user,require_roles
from app.models.entities import User,Role,FederatedQueryJob,FederatedJoinDefinition,QueryQuota,QueryUsageLedger
from app.services.federated_query import create_job,cancel_job,execute_job,page_job,decode_cursor
router=APIRouter(prefix='/api/v017',tags=['V017 Federated Query Mesh'])
GOV=(Role.admin,Role.director,Role.data_engineer,Role.data_steward)
class JobIn(BaseModel):
 model_id:str|None=None; join_id:str|None=None; metrics:list[str]=Field(default_factory=list); dimensions:list[str]=Field(default_factory=list); filters:dict=Field(default_factory=dict); page_size:int=500; use_cache:bool=True
class JoinIn(BaseModel):
 name:str; left_model_id:str; right_model_id:str; join_type:str='inner'; left_key:str; right_key:str; cardinality:str='many_to_one'; max_rows_per_side:int=100000
class QuotaIn(BaseModel):
 subject_type:str='role'; subject_value:str='viewer'; max_concurrent:int=3; daily_row_limit:int=1000000; daily_cost_limit:int=1000000; max_export_rows:int=100000
@router.get('/summary')
def summary(db:Session=Depends(get_db),user:User=Depends(current_user)):
 return {'jobs':len(db.scalars(select(FederatedQueryJob)).all()),'joins':len(db.scalars(select(FederatedJoinDefinition)).all()),'quotas':len(db.scalars(select(QueryQuota)).all()),'usage_entries':len(db.scalars(select(QueryUsageLedger)).all())}
@router.post('/jobs')
def submit(p:JobIn,run_now:bool=False,db:Session=Depends(get_db),user:User=Depends(current_user)):
 try:
  job=create_job(db,user,p.model_dump())
  if run_now: job=execute_job(db,user,job)
  return job
 except PermissionError as e: raise HTTPException(429,str(e))
 except ValueError as e: raise HTTPException(400,str(e))
 except Exception as e: raise HTTPException(502,str(e)[:500])
@router.get('/jobs')
def jobs(limit:int=100,db:Session=Depends(get_db),user:User=Depends(current_user)):
 q=select(FederatedQueryJob).order_by(FederatedQueryJob.created_at.desc()).limit(min(max(limit,1),500))
 if user.role not in GOV and user.role!=Role.auditor: q=q.where(FederatedQueryJob.user_id==user.id)
 return db.scalars(q).all()
@router.post('/jobs/{job_id}/execute')
def run(job_id:str,db:Session=Depends(get_db),user:User=Depends(current_user)):
 job=db.get(FederatedQueryJob,job_id)
 if not job or (job.user_id!=user.id and user.role not in GOV): raise HTTPException(404,'Job não encontrado')
 try: return execute_job(db,user,job)
 except ValueError as e: job.status='failed'; job.error=str(e)[:2000]; db.commit(); raise HTTPException(400,str(e))
@router.post('/jobs/{job_id}/cancel')
def cancel(job_id:str,db:Session=Depends(get_db),user:User=Depends(current_user)):
 job=db.get(FederatedQueryJob,job_id)
 if not job: raise HTTPException(404,'Job não encontrado')
 try: return cancel_job(db,user,job)
 except PermissionError as e: raise HTTPException(403,str(e))
@router.get('/jobs/{job_id}/page')
def page(job_id:str,cursor:str|None=None,page_size:int=500,db:Session=Depends(get_db),user:User=Depends(current_user)):
 job=db.get(FederatedQueryJob,job_id)
 if not job or (job.user_id!=user.id and user.role not in GOV and user.role!=Role.auditor): raise HTTPException(404,'Job não encontrado')
 offset=0
 if cursor:
  try: cid,offset=decode_cursor(cursor)
  except ValueError as e: raise HTTPException(400,str(e))
  if cid!=job.id: raise HTTPException(400,'Cursor não pertence ao job')
 return page_job(job,offset,min(max(page_size,1),5000))
@router.post('/joins')
def create_join(p:JoinIn,db:Session=Depends(get_db),user:User=Depends(require_roles(*GOV))):
 if p.join_type not in {'inner','left','right','full'}: raise HTTPException(400,'Tipo de join inválido')
 obj=FederatedJoinDefinition(**p.model_dump(),owner_id=user.id); db.add(obj); db.commit(); db.refresh(obj); return obj
@router.get('/joins')
def joins(db:Session=Depends(get_db),user:User=Depends(current_user)): return db.scalars(select(FederatedJoinDefinition)).all()
@router.post('/quotas')
def create_quota(p:QuotaIn,db:Session=Depends(get_db),user:User=Depends(require_roles(*GOV))):
 obj=QueryQuota(**p.model_dump(),created_by=user.id); db.add(obj); db.commit(); db.refresh(obj); return obj
@router.get('/quotas')
def quotas(db:Session=Depends(get_db),user:User=Depends(require_roles(*GOV))): return db.scalars(select(QueryQuota)).all()
@router.get('/usage')
def usage(limit:int=200,db:Session=Depends(get_db),user:User=Depends(current_user)):
 q=select(QueryUsageLedger).order_by(QueryUsageLedger.created_at.desc()).limit(min(max(limit,1),1000))
 if user.role not in GOV and user.role!=Role.auditor: q=q.where(QueryUsageLedger.user_id==user.id)
 return db.scalars(q).all()
