from datetime import datetime, timedelta, timezone
from sqlalchemy import select, func
from sqlalchemy.orm import Session
from app.core.security import hash_password
from app.models.entities import (
    User, Role, AuditLog, Tenant, TenantMembership, SaaSPlan, SaaSSubscription,
    SaaSModule, TenantEntitlement, TenantBranding, TenantUsageMeter,
    TenantProvisioningRun,
)

DEFAULT_MODULES = [
    ('data_lake','Data Lake'),('bi','Business Intelligence'),('ai','Enterprise AI'),
    ('etl','ETL/ELT'),('workflow','Workflow'),('api_studio','API Studio'),
    ('communication','Communication Hub'),('semantic_layer','Semantic Layer'),
]

def seed_catalog(db: Session) -> None:
    if not db.scalar(select(SaaSPlan).where(SaaSPlan.code=='enterprise')):
        db.add(SaaSPlan(code='enterprise',name='Enterprise',monthly_price_cents=0,
            limits_json={'users':1000,'storage_gb':1000,'ai_tokens':10000000,'query_units':10000000}))
    for code,name in DEFAULT_MODULES:
        if not db.scalar(select(SaaSModule).where(SaaSModule.code==code)):
            db.add(SaaSModule(code=code,name=name,description=f'Módulo SaaS {name}'))
    db.commit()

def provision_tenant(db: Session, actor: User, payload: dict) -> dict:
    slug=payload['slug'].strip().lower()
    if db.scalar(select(Tenant).where(Tenant.slug==slug)):
        raise ValueError('Slug já utilizado')
    if db.scalar(select(User).where(User.email==payload['admin_email'].lower())):
        raise ValueError('E-mail do administrador já cadastrado')
    seed_catalog(db)
    run=TenantProvisioningRun(requested_by=actor.id,status='running',steps_json=[])
    db.add(run); db.flush()
    steps=[]
    try:
        tenant=Tenant(slug=slug,legal_name=payload['legal_name'],trade_name=payload.get('trade_name',''),
            tax_id=payload.get('tax_id',''),isolation_mode=payload.get('isolation_mode','shared_schema'),
            region=payload.get('region','sa-east-1'),default_locale=payload.get('default_locale','pt-BR'),
            default_timezone=payload.get('default_timezone','America/Sao_Paulo'),status='provisioning')
        db.add(tenant); db.flush(); run.tenant_id=tenant.id; steps.append({'step':'tenant','status':'success'})
        admin=User(email=payload['admin_email'].lower(),password_hash=hash_password(payload['admin_password']),
            full_name=payload.get('admin_name','Tenant Administrator'),role=Role.admin)
        db.add(admin); db.flush(); steps.append({'step':'admin_user','status':'success'})
        db.add(TenantMembership(tenant_id=tenant.id,user_id=admin.id,tenant_role='owner',permissions=['*']))
        plan=db.scalar(select(SaaSPlan).where(SaaSPlan.code==payload.get('plan_code','enterprise')))
        if not plan: raise ValueError('Plano não encontrado')
        now=datetime.now(timezone.utc)
        db.add(SaaSSubscription(tenant_id=tenant.id,plan_id=plan.id,status='trialing',seats=payload.get('seats',10),
            trial_ends_at=now+timedelta(days=payload.get('trial_days',14)),current_period_start=now,
            current_period_end=now+timedelta(days=payload.get('trial_days',14))))
        steps.append({'step':'subscription','status':'success'})
        enabled=set(payload.get('modules') or [x[0] for x in DEFAULT_MODULES])
        for module in db.scalars(select(SaaSModule).where(SaaSModule.enabled==True)).all():
            db.add(TenantEntitlement(tenant_id=tenant.id,module_id=module.id,enabled=module.code in enabled))
        steps.append({'step':'entitlements','status':'success'})
        db.add(TenantBranding(tenant_id=tenant.id,product_name=payload.get('product_name',tenant.trade_name or tenant.legal_name),
            primary_color=payload.get('primary_color','#16324F'),secondary_color=payload.get('secondary_color','#2E7D32'),
            support_email=payload.get('support_email',payload['admin_email'])))
        tenant.status='active'; tenant.activated_at=now
        run.status='success'; run.steps_json=steps; run.finished_at=now
        db.add(AuditLog(actor=actor.email,action='tenant.provision',entity_type='tenant',entity_id=tenant.id,
            details={'slug':slug,'plan':plan.code,'modules':sorted(enabled)}))
        db.commit()
        return {'tenant_id':tenant.id,'admin_user_id':admin.id,'status':tenant.status,'provisioning_run_id':run.id}
    except Exception as exc:
        db.rollback()
        raise

def resolve_membership(db: Session, user: User, tenant_id: str) -> TenantMembership | None:
    return db.scalar(select(TenantMembership).where(TenantMembership.tenant_id==tenant_id,
        TenantMembership.user_id==user.id,TenantMembership.status=='active'))

def meter_usage(db: Session, tenant_id: str, metric_code: str, quantity: int, unit: str='unit', dimensions: dict|None=None):
    obj=TenantUsageMeter(tenant_id=tenant_id,metric_code=metric_code,quantity=max(quantity,0),unit=unit,dimensions=dimensions or {})
    db.add(obj); db.commit(); db.refresh(obj); return obj

def tenant_summary(db: Session, tenant_id: str) -> dict:
    tenant=db.get(Tenant,tenant_id)
    if not tenant: raise ValueError('Tenant não encontrado')
    return {
        'tenant':tenant,
        'members':db.scalar(select(func.count(TenantMembership.id)).where(TenantMembership.tenant_id==tenant_id)) or 0,
        'enabled_modules':db.scalar(select(func.count(TenantEntitlement.id)).where(TenantEntitlement.tenant_id==tenant_id,TenantEntitlement.enabled==True)) or 0,
        'usage_events':db.scalar(select(func.count(TenantUsageMeter.id)).where(TenantUsageMeter.tenant_id==tenant_id)) or 0,
    }
