from pathlib import Path
from fastapi import APIRouter,Depends,HTTPException
from fastapi.responses import FileResponse
from pydantic import BaseModel
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,FederatedExecutionProfile,FederatedSourceExecution,ObjectStorageArtifact
from app.services.federated_orchestrator import execute_adaptive,export_advanced,presigned_reference
router=APIRouter(prefix='/api/v019',tags=['V019 Adaptive Federated Orchestrator'])
OPS=(Role.admin,Role.director,Role.data_engineer)
class ExecuteIn(BaseModel): worker_name:str='adaptive-worker'
class ProfileIn(BaseModel): name:str; subject_type:str='role'; subject_value:str='viewer'; strategy:str='auto'; parallel_sources:bool=True; memory_limit_mb:int=512; sort_merge_threshold_rows:int=250000; pushdown_enabled:bool=True; source_timeout_seconds:int=120
class ExportIn(BaseModel): format:str='parquet'; ttl_hours:int=24
@router.get('/summary')
def summary(db:Session=Depends(get_db),user:User=Depends(current_user)):
 return {'profiles':len(db.scalars(select(FederatedExecutionProfile)).all()),'source_executions':len(db.scalars(select(FederatedSourceExecution)).all()),'artifacts':len(db.scalars(select(ObjectStorageArtifact)).all())}
@router.post('/jobs/{job_id}/execute')
def execute(job_id:str,p:ExecuteIn,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 OPS): raise HTTPException(404,'Job não encontrado')
 try:return execute_adaptive(db,user,job,p.worker_name)
 except ValueError as e: raise HTTPException(400,str(e))
@router.post('/profiles')
def create_profile(p:ProfileIn,db:Session=Depends(get_db),user:User=Depends(require_roles(*OPS))):
 obj=FederatedExecutionProfile(**p.model_dump(),created_by=user.id); db.add(obj); db.commit(); db.refresh(obj); return obj
@router.get('/profiles')
def profiles(db:Session=Depends(get_db),user:User=Depends(require_roles(*OPS))): return db.scalars(select(FederatedExecutionProfile).order_by(FederatedExecutionProfile.created_at.desc())).all()
@router.get('/source-executions')
def sources(limit:int=100,db:Session=Depends(get_db),user:User=Depends(require_roles(*OPS,Role.auditor))): return db.scalars(select(FederatedSourceExecution).order_by(FederatedSourceExecution.created_at.desc()).limit(min(max(limit,1),500))).all()
@router.post('/jobs/{job_id}/advanced-export')
def advanced_export(job_id:str,p:ExportIn,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 OPS): raise HTTPException(404,'Job não encontrado')
 try:return export_advanced(db,user,job,p.format,p.ttl_hours)
 except ValueError as e: raise HTTPException(400,str(e))
@router.get('/artifacts')
def artifacts(limit:int=100,db:Session=Depends(get_db),user:User=Depends(current_user)):
 q=select(ObjectStorageArtifact).order_by(ObjectStorageArtifact.created_at.desc()).limit(min(max(limit,1),500))
 if user.role not in OPS and user.role!=Role.auditor:q=q.where(ObjectStorageArtifact.owner_id==user.id)
 return db.scalars(q).all()
@router.post('/artifacts/{artifact_id}/presign')
def presign(artifact_id:str,minutes:int=15,db:Session=Depends(get_db),user:User=Depends(current_user)):
 obj=db.get(ObjectStorageArtifact,artifact_id)
 if not obj or (obj.owner_id!=user.id and user.role not in OPS): raise HTTPException(404,'Artefato não encontrado')
 return presigned_reference(obj,minutes)
@router.get('/artifacts/{artifact_id}/download')
def download(artifact_id:str,db:Session=Depends(get_db),user:User=Depends(current_user)):
 obj=db.get(ObjectStorageArtifact,artifact_id)
 if not obj or (obj.owner_id!=user.id and user.role not in OPS): raise HTTPException(404,'Artefato não encontrado')
 path=Path(obj.object_key)
 if not path.is_file(): raise HTTPException(410,'Artefato indisponível')
 return FileResponse(path,filename=path.name,media_type=obj.content_type)
