from datetime import datetime
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 BIThemeV036,BIDashboardV036,BIWidgetV036,ReportTemplateV036,ReportRenderV036,BIShareLinkV036,Tenant,User
from app.services.bi_report_v036 import VISUAL_TYPES,create_share_link,render_report,resolve_share_link,validate_report_template,validate_widget
from app.services.saas_tenancy import resolve_membership
router=APIRouter(prefix='/api/v036',tags=['V036 Advanced BI and Report Designer'])
ADMIN={'admin','director','manager','data_steward','data_engineer'}
def ctx(x_tenant_id:str|None=Header(None,alias='X-Tenant-ID'),db:Session=Depends(get_db),user:User=Depends(current_user)):
    if not x_tenant_id: raise HTTPException(400,'X-Tenant-ID obrigatório')
    tenant=db.get(Tenant,x_tenant_id)
    if not tenant or tenant.status!='active': raise HTTPException(404,'Tenant ativo não encontrado')
    if getattr(user.role,'value',user.role) not in {'admin','director'} and not resolve_membership(db,user,tenant.id): raise HTTPException(403,'Usuário não pertence ao tenant')
    return tenant,user
def admin(c=Depends(ctx)):
    if getattr(c[1].role,'value',c[1].role) not in ADMIN: raise HTTPException(403,'Perfil de gestão ou dados necessário')
    return c
class ThemeIn(BaseModel): code:str=Field(min_length=2,max_length=80); name:str; tokens:dict=Field(default_factory=dict); is_default:bool=False
class DashboardIn(BaseModel): name:str; description:str=''; semantic_model_id:str|None=None; lakehouse_table_id:str|None=None; theme_id:str|None=None; layout:dict=Field(default_factory=dict); filters:list=Field(default_factory=list); bookmarks:list=Field(default_factory=list); responsive:bool=True; department_id:str|None=None
class WidgetIn(BaseModel): title:str; visual_type:str; query_definition:dict=Field(default_factory=dict); visual_config:dict=Field(default_factory=dict); position:dict; interactions:dict=Field(default_factory=dict); drill_config:dict=Field(default_factory=dict); conditional_formatting:list=Field(default_factory=list); sort_order:int=0
class TemplateIn(BaseModel): name:str; description:str=''; page_config:dict=Field(default_factory=lambda:{'size':'A4','orientation':'portrait'}); sections:list; parameters:list=Field(default_factory=list); data_definition:dict=Field(default_factory=dict); output_formats:list=Field(default_factory=lambda:['html','csv','xlsx']); approval_required:bool=False
class RenderIn(BaseModel): output_format:str='html'; parameters:dict=Field(default_factory=dict)
class ShareIn(BaseModel): expires_at:datetime|None=None; allowed_domains:list[str]=Field(default_factory=list); require_authentication:bool=True
@router.get('/summary')
def summary(c=Depends(ctx),db:Session=Depends(get_db)):
    tid=c[0].id
    return {'version':'V036','visual_types':sorted(VISUAL_TYPES),'themes':db.scalar(select(func.count()).select_from(BIThemeV036).where(BIThemeV036.tenant_id==tid)) or 0,'dashboards':db.scalar(select(func.count()).select_from(BIDashboardV036).where(BIDashboardV036.tenant_id==tid)) or 0,'templates':db.scalar(select(func.count()).select_from(ReportTemplateV036).where(ReportTemplateV036.tenant_id==tid)) or 0}
@router.post('/themes',status_code=201)
def theme(p:ThemeIn,c=Depends(admin),db:Session=Depends(get_db)):
    if p.is_default: db.query(BIThemeV036).filter(BIThemeV036.tenant_id==c[0].id).update({'is_default':False})
    item=BIThemeV036(tenant_id=c[0].id,created_by=c[1].id,**p.model_dump()); db.add(item); db.commit(); db.refresh(item); return item
@router.get('/themes')
def themes(c=Depends(ctx),db:Session=Depends(get_db)): return db.scalars(select(BIThemeV036).where(BIThemeV036.tenant_id==c[0].id).order_by(BIThemeV036.name)).all()
@router.post('/dashboards',status_code=201)
def dashboard(p:DashboardIn,c=Depends(admin),db:Session=Depends(get_db)):
    item=BIDashboardV036(tenant_id=c[0].id,owner_id=c[1].id,**p.model_dump()); db.add(item); db.commit(); db.refresh(item); return item
@router.get('/dashboards')
def dashboards(c=Depends(ctx),db:Session=Depends(get_db)): return db.scalars(select(BIDashboardV036).where(BIDashboardV036.tenant_id==c[0].id).order_by(BIDashboardV036.updated_at.desc())).all()
def owned_dashboard(db,tid,did):
    d=db.get(BIDashboardV036,did)
    if not d or d.tenant_id!=tid: raise HTTPException(404,'Dashboard não encontrado')
    return d
@router.post('/dashboards/{dashboard_id}/widgets',status_code=201)
def widget(dashboard_id:str,p:WidgetIn,c=Depends(admin),db:Session=Depends(get_db)):
    owned_dashboard(db,c[0].id,dashboard_id)
    try: validate_widget(p.visual_type,p.query_definition,p.position,p.drill_config)
    except ValueError as e: raise HTTPException(400,str(e))
    item=BIWidgetV036(tenant_id=c[0].id,dashboard_id=dashboard_id,created_by=c[1].id,**p.model_dump()); db.add(item); db.commit(); db.refresh(item); return item
@router.get('/dashboards/{dashboard_id}/widgets')
def widgets(dashboard_id:str,c=Depends(ctx),db:Session=Depends(get_db)):
    owned_dashboard(db,c[0].id,dashboard_id); return db.scalars(select(BIWidgetV036).where(BIWidgetV036.dashboard_id==dashboard_id).order_by(BIWidgetV036.sort_order)).all()
@router.post('/dashboards/{dashboard_id}/publish')
def publish(dashboard_id:str,c=Depends(admin),db:Session=Depends(get_db)):
    d=owned_dashboard(db,c[0].id,dashboard_id); count=db.scalar(select(func.count()).select_from(BIWidgetV036).where(BIWidgetV036.dashboard_id==d.id)) or 0
    if count<1: raise HTTPException(400,'Adicione ao menos um widget antes de publicar')
    d.status='published'; db.commit(); db.refresh(d); return d
@router.post('/dashboards/{dashboard_id}/share',status_code=201)
def share(dashboard_id:str,p:ShareIn,c=Depends(admin),db:Session=Depends(get_db)):
    d=owned_dashboard(db,c[0].id,dashboard_id); item,raw=create_share_link(db,d,c[1].id,p.expires_at,p.allowed_domains,p.require_authentication); return {'id':item.id,'token':raw,'expires_at':item.expires_at,'path':f'/bi-shared/{raw}'}
@router.post('/report-templates',status_code=201)
def template(p:TemplateIn,c=Depends(admin),db:Session=Depends(get_db)):
    try: validate_report_template(p.page_config,p.sections,p.parameters,p.output_formats)
    except ValueError as e: raise HTTPException(400,str(e))
    item=ReportTemplateV036(tenant_id=c[0].id,owner_id=c[1].id,**p.model_dump()); db.add(item); db.commit(); db.refresh(item); return item
@router.get('/report-templates')
def templates(c=Depends(ctx),db:Session=Depends(get_db)): return db.scalars(select(ReportTemplateV036).where(ReportTemplateV036.tenant_id==c[0].id).order_by(ReportTemplateV036.updated_at.desc())).all()
@router.post('/report-templates/{template_id}/render',status_code=201)
def render(template_id:str,p:RenderIn,c=Depends(ctx),db:Session=Depends(get_db)):
    t=db.get(ReportTemplateV036,template_id)
    if not t or t.tenant_id!=c[0].id: raise HTTPException(404,'Template não encontrado')
    if t.approval_required and t.status!='approved': raise HTTPException(409,'Template exige aprovação')
    try: return render_report(db,t,c[1].id,p.output_format,p.parameters)
    except ValueError as e: raise HTTPException(400,str(e))
@router.get('/renders')
def renders(c=Depends(ctx),db:Session=Depends(get_db)): return db.scalars(select(ReportRenderV036).where(ReportRenderV036.tenant_id==c[0].id).order_by(ReportRenderV036.created_at.desc()).limit(100)).all()
@router.get('/share/{token}')
def shared(token:str,db:Session=Depends(get_db)):
    item=resolve_share_link(db,token)
    if not item: raise HTTPException(404,'Link inválido ou expirado')
    d=db.get(BIDashboardV036,item.dashboard_id)
    return {'dashboard':d,'widgets':db.scalars(select(BIWidgetV036).where(BIWidgetV036.dashboard_id==d.id).order_by(BIWidgetV036.sort_order)).all(),'require_authentication':item.require_authentication}
