from __future__ import annotations
from datetime import datetime, timezone
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel, Field
from sqlalchemy import func, select
from sqlalchemy.orm import Session
from app.api.deps import current_user, require_roles
from app.core.database import get_db
from app.models.entities import (DataCatalogEntry, Dataset, EnterpriseQualityRule, QualityIncident,
 QualityProfile, QualityResult, QualityRun, QuarantineRecord, RemediationPlan, Role, User)
from app.services.analytics import latest_frame
from app.services.data_quality import detect_anomalies, execute_quality_run, profile_frame

router=APIRouter(prefix='/api/v012',tags=['V012 Enterprise Data Quality'])
ELEVATED=(Role.admin,Role.director,Role.data_engineer,Role.data_steward)
def now(): return datetime.now(timezone.utc)

class RuleIn(BaseModel):
    dataset_id:str; name:str=Field(min_length=2,max_length=180); description:str=''; dimension:str='validity'; rule_type:str; column_name:str|None=None; config:dict={}; severity:str='warning'; weight:int=10; schedule_cron:str|None=None; quarantine_on_failure:bool=False; enabled:bool=True
class IncidentUpdate(BaseModel): status:str; owner_id:str|None=None; resolution:str=''
class RemediationIn(BaseModel): title:str; actions:list[dict]=[]; owner_id:str; target_date:datetime|None=None
class RemediationUpdate(BaseModel): status:str|None=None; progress:int|None=None; actions:list[dict]|None=None

@router.get('/summary')
def summary(db:Session=Depends(get_db),u:User=Depends(current_user)):
    runs=db.scalars(select(QualityRun).order_by(QualityRun.started_at.desc()).limit(100)).all(); latest={}
    for r in runs:
        if r.dataset_id not in latest: latest[r.dataset_id]=r
    incidents=db.query(QualityIncident).filter(QualityIncident.status!='resolved').count(); quarantined=db.query(QuarantineRecord).filter_by(status='quarantined').count()
    scores=[x.overall_score for x in latest.values()]
    return {'version':'V012','monitored_datasets':len(latest),'average_score':round(sum(scores)/len(scores),1) if scores else 0,'open_incidents':incidents,'quarantined_records':quarantined,'latest_runs':[{'id':x.id,'dataset_id':x.dataset_id,'score':x.overall_score,'status':x.status,'started_at':x.started_at} for x in list(latest.values())[:20]]}

@router.post('/datasets/{dataset_id}/profile')
def create_profile(dataset_id:str,db:Session=Depends(get_db),u:User=Depends(require_roles(*ELEVATED))):
    dataset=db.get(Dataset,dataset_id)
    if not dataset: raise HTTPException(404,'Dataset não encontrado')
    df=latest_frame(db,dataset); profile=profile_frame(df); anomalies=detect_anomalies(df)
    p=QualityProfile(dataset_id=dataset.id,row_count=len(df),column_count=len(df.columns),profile=profile,anomaly_summary=anomalies,created_by=u.id); db.add(p); db.commit(); db.refresh(p)
    return {'id':p.id,'dataset_id':p.dataset_id,'profile':profile,'anomalies':anomalies,'created_at':p.created_at}

@router.get('/datasets/{dataset_id}/profiles')
def profiles(dataset_id:str,limit:int=20,db:Session=Depends(get_db),u:User=Depends(current_user)):
    rows=db.scalars(select(QualityProfile).where(QualityProfile.dataset_id==dataset_id).order_by(QualityProfile.created_at.desc()).limit(min(limit,100))).all()
    return [{'id':x.id,'row_count':x.row_count,'column_count':x.column_count,'profile':x.profile,'anomaly_summary':x.anomaly_summary,'created_at':x.created_at} for x in rows]

@router.post('/rules')
def create_rule(p:RuleIn,db:Session=Depends(get_db),u:User=Depends(require_roles(*ELEVATED))):
    if not db.get(Dataset,p.dataset_id): raise HTTPException(404,'Dataset não encontrado')
    allowed={'not_null','unique','accepted_values','regex','range','length','freshness','row_count','completeness'}
    if p.rule_type not in allowed: raise HTTPException(422,'Tipo de regra não suportado')
    r=EnterpriseQualityRule(**p.model_dump(),owner_id=u.id); db.add(r); db.commit(); db.refresh(r); return {'id':r.id,'status':r.last_status}

@router.get('/rules')
def list_rules(dataset_id:str|None=None,db:Session=Depends(get_db),u:User=Depends(current_user)):
    q=select(EnterpriseQualityRule)
    if dataset_id: q=q.where(EnterpriseQualityRule.dataset_id==dataset_id)
    rows=db.scalars(q.order_by(EnterpriseQualityRule.created_at.desc()).limit(500)).all()
    return [{'id':x.id,'dataset_id':x.dataset_id,'name':x.name,'dimension':x.dimension,'rule_type':x.rule_type,'column_name':x.column_name,'config':x.config,'severity':x.severity,'weight':x.weight,'quarantine_on_failure':x.quarantine_on_failure,'enabled':x.enabled,'last_status':x.last_status,'last_score':x.last_score,'last_run_at':x.last_run_at} for x in rows]

@router.post('/datasets/{dataset_id}/run')
def run_quality(dataset_id:str,db:Session=Depends(get_db),u:User=Depends(require_roles(*ELEVATED))):
    dataset=db.get(Dataset,dataset_id)
    if not dataset: raise HTTPException(404,'Dataset não encontrado')
    run=execute_quality_run(db,dataset,u)
    if run.status=='completed':
        catalog=db.scalar(select(DataCatalogEntry).where(DataCatalogEntry.dataset_id==dataset.id))
        if catalog: catalog.quality_score=run.overall_score; db.commit()
    return {'id':run.id,'status':run.status,'score':run.overall_score,'dimension_scores':run.dimension_scores,'passed':run.passed_rules,'failed':run.failed_rules,'error':run.error}

@router.get('/runs')
def list_runs(dataset_id:str|None=None,limit:int=100,db:Session=Depends(get_db),u:User=Depends(current_user)):
    q=select(QualityRun)
    if dataset_id: q=q.where(QualityRun.dataset_id==dataset_id)
    rows=db.scalars(q.order_by(QualityRun.started_at.desc()).limit(min(limit,500))).all()
    return [{'id':x.id,'dataset_id':x.dataset_id,'trigger_type':x.trigger_type,'status':x.status,'overall_score':x.overall_score,'dimension_scores':x.dimension_scores,'total_rules':x.total_rules,'passed_rules':x.passed_rules,'failed_rules':x.failed_rules,'started_at':x.started_at,'finished_at':x.finished_at,'error':x.error} for x in rows]

@router.get('/runs/{run_id}')
def run_detail(run_id:str,db:Session=Depends(get_db),u:User=Depends(current_user)):
    run=db.get(QualityRun,run_id)
    if not run: raise HTTPException(404,'Execução não encontrada')
    results=db.scalars(select(QualityResult).where(QualityResult.quality_run_id==run.id)).all()
    return {'run':{'id':run.id,'dataset_id':run.dataset_id,'status':run.status,'overall_score':run.overall_score,'dimension_scores':run.dimension_scores},'results':[{'id':x.id,'rule_id':x.rule_id,'status':x.status,'score':x.score,'checked_rows':x.checked_rows,'failed_rows':x.failed_rows,'failure_rate':x.failure_rate,'sample_failures':x.sample_failures,'metrics':x.metrics} for x in results]}

@router.get('/incidents')
def incidents(status:str|None='open',severity:str|None=None,db:Session=Depends(get_db),u:User=Depends(current_user)):
    q=select(QualityIncident)
    if status: q=q.where(QualityIncident.status==status)
    if severity: q=q.where(QualityIncident.severity==severity)
    rows=db.scalars(q.order_by(QualityIncident.created_at.desc()).limit(500)).all()
    return [{'id':x.id,'dataset_id':x.dataset_id,'quality_run_id':x.quality_run_id,'rule_id':x.rule_id,'title':x.title,'description':x.description,'severity':x.severity,'status':x.status,'owner_id':x.owner_id,'due_at':x.due_at,'resolution':x.resolution,'created_at':x.created_at} for x in rows]

@router.patch('/incidents/{incident_id}')
def update_incident(incident_id:str,p:IncidentUpdate,db:Session=Depends(get_db),u:User=Depends(require_roles(*ELEVATED))):
    x=db.get(QualityIncident,incident_id)
    if not x: raise HTTPException(404,'Incidente não encontrado')
    x.status=p.status; x.owner_id=p.owner_id or x.owner_id; x.resolution=p.resolution
    if p.status=='resolved': x.resolved_by=u.id; x.resolved_at=now()
    db.commit(); return {'status':x.status}

@router.post('/incidents/{incident_id}/remediation')
def create_remediation(incident_id:str,p:RemediationIn,db:Session=Depends(get_db),u:User=Depends(require_roles(*ELEVATED))):
    if not db.get(QualityIncident,incident_id): raise HTTPException(404,'Incidente não encontrado')
    plan=RemediationPlan(incident_id=incident_id,created_by=u.id,**p.model_dump()); db.add(plan); db.commit(); return {'id':plan.id}

@router.patch('/remediation/{plan_id}')
def update_remediation(plan_id:str,p:RemediationUpdate,db:Session=Depends(get_db),u:User=Depends(require_roles(*ELEVATED))):
    plan=db.get(RemediationPlan,plan_id)
    if not plan: raise HTTPException(404,'Plano não encontrado')
    if p.status is not None: plan.status=p.status
    if p.progress is not None: plan.progress=max(0,min(100,p.progress))
    if p.actions is not None: plan.actions=p.actions
    db.commit(); return {'status':plan.status,'progress':plan.progress}

@router.get('/quarantine')
def quarantine(dataset_id:str|None=None,status:str='quarantined',db:Session=Depends(get_db),u:User=Depends(current_user)):
    q=select(QuarantineRecord).where(QuarantineRecord.status==status)
    if dataset_id: q=q.where(QuarantineRecord.dataset_id==dataset_id)
    rows=db.scalars(q.order_by(QuarantineRecord.created_at.desc()).limit(500)).all()
    return [{'id':x.id,'dataset_id':x.dataset_id,'quality_run_id':x.quality_run_id,'rule_id':x.rule_id,'row_reference':x.row_reference,'row_data':x.row_data,'reason':x.reason,'status':x.status,'created_at':x.created_at} for x in rows]

@router.post('/quarantine/{record_id}/release')
def release(record_id:str,db:Session=Depends(get_db),u:User=Depends(require_roles(*ELEVATED))):
    x=db.get(QuarantineRecord,record_id)
    if not x: raise HTTPException(404,'Registro não encontrado')
    x.status='released'; x.released_by=u.id; x.released_at=now(); db.commit(); return {'status':x.status}
