from pathlib import Path
from fastapi import APIRouter,Depends,HTTPException
from fastapi.responses import FileResponse
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,FederatedWorkerRun,QuerySpillSegment,QueryExport
from app.services.federated_worker import execute_federated_job,resume_worker_run,create_export
router=APIRouter(prefix='/api/v018',tags=['V018 Distributed Federated Worker'])
OPS=(Role.admin,Role.director,Role.data_engineer)
class WorkerIn(BaseModel): memory_limit_mb:int=256; worker_name:str='federated-worker'
class ExportIn(BaseModel): format:str='csv'; ttl_hours:int=24
@router.get('/summary')
def summary(db:Session=Depends(get_db),user:User=Depends(current_user)):
 return {'worker_runs':len(db.scalars(select(FederatedWorkerRun)).all()),'spill_segments':len(db.scalars(select(QuerySpillSegment)).all()),'exports':len(db.scalars(select(QueryExport)).all())}
@router.post('/jobs/{job_id}/worker-execute')
def worker_execute(job_id:str,p:WorkerIn,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_federated_job(db,user,job,p.memory_limit_mb,p.worker_name)
 except ValueError as e: raise HTTPException(400,str(e))
 except Exception as e: raise HTTPException(502,str(e)[:500])
@router.post('/jobs/{job_id}/enqueue')
def enqueue(job_id:str,p:WorkerIn,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')
 from app.workers.tasks import execute_federated_job_v018_task
 task=execute_federated_job_v018_task.delay(job.id,user.id,p.memory_limit_mb,p.worker_name)
 return {'job_id':job.id,'task_id':task.id,'status':'queued'}
@router.get('/worker-runs')
def worker_runs(limit:int=100,db:Session=Depends(get_db),user:User=Depends(current_user)):
 q=select(FederatedWorkerRun).join(FederatedQueryJob,FederatedWorkerRun.job_id==FederatedQueryJob.id).order_by(FederatedWorkerRun.created_at.desc()).limit(min(max(limit,1),500))
 if user.role not in OPS and user.role!=Role.auditor: q=q.where(FederatedQueryJob.user_id==user.id)
 return db.scalars(q).all()
@router.post('/worker-runs/{run_id}/resume')
def resume(run_id:str,db:Session=Depends(get_db),user:User=Depends(require_roles(*OPS))):
 run=db.get(FederatedWorkerRun,run_id)
 if not run: raise HTTPException(404,'Execução não encontrada')
 try: return resume_worker_run(db,user,run)
 except ValueError as e: raise HTTPException(400,str(e))
@router.post('/jobs/{job_id}/exports')
def export_job(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 create_export(db,user,job,p.format,p.ttl_hours)
 except ValueError as e: raise HTTPException(400,str(e))
@router.get('/exports')
def exports(limit:int=100,db:Session=Depends(get_db),user:User=Depends(current_user)):
 q=select(QueryExport).order_by(QueryExport.created_at.desc()).limit(min(max(limit,1),500))
 if user.role not in OPS and user.role!=Role.auditor: q=q.where(QueryExport.user_id==user.id)
 return db.scalars(q).all()
@router.get('/exports/{export_id}/download')
def download(export_id:str,db:Session=Depends(get_db),user:User=Depends(current_user)):
 obj=db.get(QueryExport,export_id)
 if not obj or (obj.user_id!=user.id and user.role not in OPS): raise HTTPException(404,'Exportação não encontrada')
 path=Path(obj.object_key)
 if obj.status!='success' or not path.is_file(): raise HTTPException(409,'Arquivo indisponível')
 return FileResponse(path,filename=path.name,media_type='application/gzip')
