from __future__ import annotations
from collections import defaultdict
from datetime import datetime, timezone
from typing import Any
from sqlalchemy import func, select
from sqlalchemy.orm import Session
from app.models.entities import (CorporateAssetV033, CorporateProcessV033, CorporateUnitV033,
    DigitalTwinRelationV033, KeyResultV033, OperatingKpiV033, OperatingRecommendationV033,
    StrategicObjectiveV033)

ALLOWED_TWIN_TYPES={'unit','user','process','asset','dataset','dashboard','report','workflow','agent','kpi','objective','semantic_model'}
ALLOWED_RELATIONS={'owns','manages','uses','produces','depends_on','measures','supports','reports_to','automates','governs'}

def build_unit_tree(units:list[CorporateUnitV033])->list[dict[str,Any]]:
    by_parent:dict[str|None,list[CorporateUnitV033]]=defaultdict(list)
    for item in units: by_parent[item.parent_unit_id].append(item)
    for values in by_parent.values(): values.sort(key=lambda x:(x.unit_type,x.name.lower()))
    def node(item:CorporateUnitV033)->dict[str,Any]:
        return {'id':item.id,'code':item.code,'name':item.name,'unit_type':item.unit_type,
                'manager_id':item.manager_id,'region':item.region,'country':item.country,
                'active':item.active,'children':[node(x) for x in by_parent.get(item.id,[])]}
    return [node(x) for x in by_parent.get(None,[])]

def compute_key_result_progress(baseline:float,target:float,current:float,direction:str='increase')->int:
    if target==baseline: return 100 if current==target else 0
    if direction=='decrease':
        progress=(baseline-current)/(baseline-target)
    else:
        progress=(current-baseline)/(target-baseline)
    return max(0,min(100,round(progress*100)))

def refresh_objective_progress(db:Session,objective:StrategicObjectiveV033)->StrategicObjectiveV033:
    results=db.scalars(select(KeyResultV033).where(KeyResultV033.objective_id==objective.id)).all()
    if not results: objective.progress_percent=0
    else:
        total_weight=sum(max(1,x.weight) for x in results)
        objective.progress_percent=round(sum(x.progress_percent*max(1,x.weight) for x in results)/total_weight)
    db.commit(); db.refresh(objective); return objective

def kpi_status(item:OperatingKpiV033)->str:
    value=item.current_value
    if item.direction=='decrease':
        if value<=item.target_value: return 'on_target'
        if item.warning_value and value<=item.warning_value: return 'warning'
        return 'critical'
    if value>=item.target_value: return 'on_target'
    if item.warning_value and value>=item.warning_value: return 'warning'
    return 'critical'

def validate_relation(source_type:str,target_type:str,relation_type:str)->list[str]:
    errors=[]
    if source_type not in ALLOWED_TWIN_TYPES: errors.append('Tipo de origem não permitido')
    if target_type not in ALLOWED_TWIN_TYPES: errors.append('Tipo de destino não permitido')
    if relation_type not in ALLOWED_RELATIONS: errors.append('Tipo de relação não permitido')
    return errors

def graph_for_tenant(db:Session,tenant_id:str)->dict[str,Any]:
    relations=db.scalars(select(DigitalTwinRelationV033).where(DigitalTwinRelationV033.tenant_id==tenant_id)).all()
    nodes={}
    edges=[]
    for r in relations:
        sk=f'{r.source_type}:{r.source_id}'; tk=f'{r.target_type}:{r.target_id}'
        nodes.setdefault(sk,{'id':sk,'type':r.source_type,'resource_id':r.source_id})
        nodes.setdefault(tk,{'id':tk,'type':r.target_type,'resource_id':r.target_id})
        edges.append({'id':r.id,'source':sk,'target':tk,'relation_type':r.relation_type,'metadata':r.metadata_json})
    return {'nodes':list(nodes.values()),'edges':edges}

def executive_cockpit(db:Session,tenant_id:str)->dict[str,Any]:
    units=db.scalar(select(func.count()).select_from(CorporateUnitV033).where(CorporateUnitV033.tenant_id==tenant_id,CorporateUnitV033.active.is_(True))) or 0
    processes=db.scalar(select(func.count()).select_from(CorporateProcessV033).where(CorporateProcessV033.tenant_id==tenant_id)) or 0
    assets=db.scalar(select(func.count()).select_from(CorporateAssetV033).where(CorporateAssetV033.tenant_id==tenant_id,CorporateAssetV033.status=='active')) or 0
    objectives=db.scalars(select(StrategicObjectiveV033).where(StrategicObjectiveV033.tenant_id==tenant_id,StrategicObjectiveV033.status=='active')).all()
    kpis=db.scalars(select(OperatingKpiV033).where(OperatingKpiV033.tenant_id==tenant_id,OperatingKpiV033.active.is_(True))).all()
    recommendations=db.scalar(select(func.count()).select_from(OperatingRecommendationV033).where(OperatingRecommendationV033.tenant_id==tenant_id,OperatingRecommendationV033.status=='proposed')) or 0
    return {'units':units,'processes':processes,'assets':assets,'objectives':len(objectives),
            'objective_progress':round(sum(x.progress_percent for x in objectives)/len(objectives)) if objectives else 0,
            'kpis':len(kpis),'kpis_on_target':sum(1 for x in kpis if x.status=='on_target'),
            'kpis_warning':sum(1 for x in kpis if x.status=='warning'),
            'kpis_critical':sum(1 for x in kpis if x.status=='critical'),
            'recommendations_pending':recommendations,'generated_at':datetime.now(timezone.utc)}
