from typing import Any
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 (DataCorrectionPolicyV031, DataQualityScorecardV031, Dataset,
    MasterMergeProposalV031, QualityCorrectionRunV031, QualityRulePackV031,
    StewardshipAssignmentV031, Tenant, User)
from app.services.data_quality_v031 import approve_merge, create_scorecard, propose_merges, run_corrections
from app.services.saas_tenancy import resolve_membership

router=APIRouter(prefix='/api/v031',tags=['V031 Advanced Data Quality & MDM'])
ELEVATED={'admin','director','data_steward','data_engineer'}
class RulePackIn(BaseModel): code:str; name:str; domain:str='general'; description:str=''; rules:list=Field(default_factory=list); enabled:bool=True
class CorrectionIn(BaseModel): dataset_id:str; name:str; column_name:str; correction_type:str; config:dict[str,Any]=Field(default_factory=dict); on_failure:str='quarantine'; priority:int=100; enabled:bool=True
class StewardIn(BaseModel): domain:str; owner_id:str|None=None; steward_id:str; technical_owner_id:str|None=None; approver_ids:list=Field(default_factory=list)
class MergeScanIn(BaseModel): entity_type:str; fields:list[str]; threshold:int=Field(default=85,ge=1,le=100)
class ScorecardIn(BaseModel): dataset_id:str; domain:str='general'

def tenant_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); role=getattr(user.role,'value',user.role)
    if not tenant or tenant.status!='active': raise HTTPException(404,'Tenant ativo não encontrado')
    membership=resolve_membership(db,user,tenant.id)
    if role not in {'admin','director'} and not membership: raise HTTPException(403,'Acesso negado')
    return tenant,user,membership

def elevated(ctx=Depends(tenant_ctx)):
    tenant,user,membership=ctx; role=getattr(user.role,'value',user.role)
    if role not in ELEVATED and getattr(membership,'tenant_role','') not in ('owner','admin'): raise HTTPException(403,'Permissão elevada necessária')
    return ctx

@router.get('/summary')
def summary(ctx=Depends(tenant_ctx),db:Session=Depends(get_db)):
    tenant,_,_=ctx
    return {'version':'V031','rule_packs':db.scalar(select(func.count()).select_from(QualityRulePackV031).where(QualityRulePackV031.tenant_id==tenant.id)) or 0,'correction_policies':db.scalar(select(func.count()).select_from(DataCorrectionPolicyV031).where(DataCorrectionPolicyV031.tenant_id==tenant.id)) or 0,'pending_merges':db.scalar(select(func.count()).select_from(MasterMergeProposalV031).where(MasterMergeProposalV031.tenant_id==tenant.id,MasterMergeProposalV031.status=='pending')) or 0,'scorecards':db.scalar(select(func.count()).select_from(DataQualityScorecardV031).where(DataQualityScorecardV031.tenant_id==tenant.id)) or 0}

@router.post('/rule-packs',status_code=201)
def create_rule_pack(p:RulePackIn,ctx=Depends(elevated),db:Session=Depends(get_db)):
    tenant,user,_=ctx; item=QualityRulePackV031(tenant_id=tenant.id,created_by=user.id,**p.model_dump()); db.add(item); db.commit(); db.refresh(item); return item
@router.get('/rule-packs')
def list_rule_packs(ctx=Depends(tenant_ctx),db:Session=Depends(get_db)):
    tenant,_,_=ctx; return db.scalars(select(QualityRulePackV031).where(QualityRulePackV031.tenant_id==tenant.id).order_by(QualityRulePackV031.name)).all()

@router.post('/correction-policies',status_code=201)
def create_correction(p:CorrectionIn,ctx=Depends(elevated),db:Session=Depends(get_db)):
    tenant,user,_=ctx
    if not db.get(Dataset,p.dataset_id): raise HTTPException(404,'Dataset não encontrado')
    item=DataCorrectionPolicyV031(tenant_id=tenant.id,created_by=user.id,**p.model_dump()); db.add(item); db.commit(); db.refresh(item); return item
@router.get('/correction-policies')
def corrections(ctx=Depends(tenant_ctx),db:Session=Depends(get_db),dataset_id:str|None=None):
    tenant,_,_=ctx; q=select(DataCorrectionPolicyV031).where(DataCorrectionPolicyV031.tenant_id==tenant.id); q=q.where(DataCorrectionPolicyV031.dataset_id==dataset_id) if dataset_id else q
    return db.scalars(q.order_by(DataCorrectionPolicyV031.priority)).all()
@router.post('/datasets/{dataset_id}/correct')
def correct(dataset_id:str,dry_run:bool=True,ctx=Depends(elevated),db:Session=Depends(get_db)):
    tenant,user,_=ctx; ds=db.get(Dataset,dataset_id)
    if not ds: raise HTTPException(404,'Dataset não encontrado')
    run=run_corrections(db,tenant.id,ds,user.id,dry_run); return run
@router.get('/correction-runs')
def correction_runs(ctx=Depends(tenant_ctx),db:Session=Depends(get_db)):
    tenant,_,_=ctx; return db.scalars(select(QualityCorrectionRunV031).where(QualityCorrectionRunV031.tenant_id==tenant.id).order_by(QualityCorrectionRunV031.created_at.desc()).limit(200)).all()

@router.post('/stewardship',status_code=201)
def stewardship(p:StewardIn,ctx=Depends(elevated),db:Session=Depends(get_db)):
    tenant,user,_=ctx; item=StewardshipAssignmentV031(tenant_id=tenant.id,created_by=user.id,**p.model_dump()); db.add(item); db.commit(); db.refresh(item); return item
@router.get('/stewardship')
def stewardship_list(ctx=Depends(tenant_ctx),db:Session=Depends(get_db)):
    tenant,_,_=ctx; return db.scalars(select(StewardshipAssignmentV031).where(StewardshipAssignmentV031.tenant_id==tenant.id,StewardshipAssignmentV031.active.is_(True))).all()

@router.post('/mdm/merge-scan')
def merge_scan(p:MergeScanIn,ctx=Depends(elevated),db:Session=Depends(get_db)):
    tenant,user,_=ctx; items=propose_merges(db,tenant.id,p.entity_type,user.id,p.fields,p.threshold); return {'created':len(items),'proposal_ids':[x.id for x in items]}
@router.get('/mdm/merge-proposals')
def merge_proposals(ctx=Depends(tenant_ctx),db:Session=Depends(get_db),status:str='pending'):
    tenant,_,_=ctx; return db.scalars(select(MasterMergeProposalV031).where(MasterMergeProposalV031.tenant_id==tenant.id,MasterMergeProposalV031.status==status).order_by(MasterMergeProposalV031.confidence_score.desc())).all()
@router.post('/mdm/merge-proposals/{proposal_id}/{decision}')
def decide_merge(proposal_id:str,decision:str,ctx=Depends(elevated),db:Session=Depends(get_db)):
    tenant,user,_=ctx; p=db.scalar(select(MasterMergeProposalV031).where(MasterMergeProposalV031.id==proposal_id,MasterMergeProposalV031.tenant_id==tenant.id))
    if not p: raise HTTPException(404,'Proposta não encontrada')
    if decision=='approve': approve_merge(db,p,user.id)
    elif decision=='reject': p.status='rejected'; p.reviewed_by=user.id; from app.models.entities import utcnow; p.reviewed_at=utcnow(); db.commit()
    else: raise HTTPException(400,'Decisão inválida')
    return {'status':p.status}

@router.post('/scorecards')
def scorecard(p:ScorecardIn,ctx=Depends(elevated),db:Session=Depends(get_db)):
    tenant,_,_=ctx
    if not db.get(Dataset,p.dataset_id): raise HTTPException(404,'Dataset não encontrado')
    return create_scorecard(db,tenant.id,p.dataset_id,p.domain)
@router.get('/scorecards')
def scorecards(ctx=Depends(tenant_ctx),db:Session=Depends(get_db)):
    tenant,_,_=ctx; return db.scalars(select(DataQualityScorecardV031).where(DataQualityScorecardV031.tenant_id==tenant.id).order_by(DataQualityScorecardV031.measured_at.desc()).limit(500)).all()
