from datetime import datetime,timezone
from pydantic import BaseModel,Field
from fastapi import APIRouter,Depends,Header,HTTPException
from sqlalchemy import select
from sqlalchemy.orm import Session
from croniter import croniter
from app.core.database import get_db
from app.api.deps import current_user
from app.models.entities import User,Tenant,ConnectorDefinition,TenantConnectorInstance,ConnectorDiscoveredObject,ConnectorSyncProfile,ConnectorTestRun
from app.services.saas_tenancy import resolve_membership
from app.services.connectivity_hub import seed_connectors,protect_config,test_instance,discover,persist_discovery
router=APIRouter(prefix='/api/v025',tags=['V025 Universal Connectivity Hub'])
ADMIN={'admin','director','data_steward','data_engineer'}

def 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); member=resolve_membership(db,user,x_tenant_id) if tenant else None
    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 member: 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 técnico necessário')
    return c
class InstanceIn(BaseModel): connector_code:str; name:str; config:dict=Field(default_factory=dict)
class SyncIn(BaseModel): name:str; selected_objects:list=Field(default_factory=list); mode:str='incremental'; watermark_column:str=''; cdc_strategy:str='watermark'; schedule_cron:str|None=None; timezone:str='America/Sao_Paulo'; destination_zone:str='bronze'; enabled:bool=True

@router.get('/summary')
def summary(c=Depends(ctx),db:Session=Depends(get_db)):
    tenant,_=c; seed_connectors(db)
    return {'version':'V025','connectors':len(db.scalars(select(ConnectorDefinition).where(ConnectorDefinition.enabled==True)).all()),'instances':len(db.scalars(select(TenantConnectorInstance).where(TenantConnectorInstance.tenant_id==tenant.id)).all()),'sync_profiles':len(db.scalars(select(ConnectorSyncProfile).where(ConnectorSyncProfile.tenant_id==tenant.id)).all())}
@router.post('/catalog/seed')
def seed(c=Depends(admin),db:Session=Depends(get_db)): seed_connectors(db); return {'status':'ok'}
@router.get('/catalog')
def catalog(category:str|None=None,db:Session=Depends(get_db)):
    seed_connectors(db); q=select(ConnectorDefinition).where(ConnectorDefinition.enabled==True)
    if category:q=q.where(ConnectorDefinition.category==category)
    return db.scalars(q.order_by(ConnectorDefinition.category,ConnectorDefinition.name)).all()
@router.post('/instances')
def create_instance(p:InstanceIn,c=Depends(admin),db:Session=Depends(get_db)):
    tenant,user=c; seed_connectors(db)
    if not db.scalar(select(ConnectorDefinition).where(ConnectorDefinition.code==p.connector_code,ConnectorDefinition.enabled==True)): raise HTTPException(404,'Conector não disponível')
    public,secret=protect_config(p.config); x=TenantConnectorInstance(tenant_id=tenant.id,connector_code=p.connector_code,name=p.name,public_config=public,encrypted_config=secret,created_by=user.id); db.add(x); db.commit(); db.refresh(x); return x
@router.get('/instances')
def instances(c=Depends(ctx),db:Session=Depends(get_db)):
    tenant,_=c; return db.scalars(select(TenantConnectorInstance).where(TenantConnectorInstance.tenant_id==tenant.id).order_by(TenantConnectorInstance.created_at.desc())).all()
@router.post('/instances/{instance_id}/test')
def test(instance_id:str,c=Depends(admin),db:Session=Depends(get_db)):
    tenant,_=c; x=db.get(TenantConnectorInstance,instance_id)
    if not x or x.tenant_id!=tenant.id: raise HTTPException(404,'Conexão não encontrada')
    run=ConnectorTestRun(tenant_id=tenant.id,connector_instance_id=x.id); db.add(run); db.commit()
    try:
        latency=test_instance(x); run.status='success'; run.latency_ms=latency; x.status='ready'; x.last_test_status='success'; x.last_test_message='Conexão validada'
    except Exception as e:
        run.status='failed'; run.error=str(e)[:1000]; x.status='error'; x.last_test_status='failed'; x.last_test_message=str(e)[:1000]
    x.last_test_at=datetime.now(timezone.utc); db.commit(); db.refresh(run); return run
@router.post('/instances/{instance_id}/discover')
def run_discovery(instance_id:str,c=Depends(admin),db:Session=Depends(get_db)):
    tenant,_=c; x=db.get(TenantConnectorInstance,instance_id)
    if not x or x.tenant_id!=tenant.id: raise HTTPException(404,'Conexão não encontrada')
    try: objects=discover(x)
    except Exception as e: raise HTTPException(400,f'Falha na descoberta: {str(e)[:500]}')
    created=persist_discovery(db,tenant.id,x.id,objects); return {'objects':len(objects),'created':created}
@router.get('/instances/{instance_id}/objects')
def objects(instance_id:str,c=Depends(ctx),db:Session=Depends(get_db)):
    tenant,_=c; return db.scalars(select(ConnectorDiscoveredObject).where(ConnectorDiscoveredObject.tenant_id==tenant.id,ConnectorDiscoveredObject.connector_instance_id==instance_id).order_by(ConnectorDiscoveredObject.qualified_name)).all()
@router.post('/instances/{instance_id}/sync-profiles')
def create_sync(instance_id:str,p:SyncIn,c=Depends(admin),db:Session=Depends(get_db)):
    tenant,user=c; x=db.get(TenantConnectorInstance,instance_id)
    if not x or x.tenant_id!=tenant.id: raise HTTPException(404,'Conexão não encontrada')
    if p.mode not in {'full','incremental','cdc'}: raise HTTPException(400,'Modo inválido')
    next_run=croniter(p.schedule_cron,datetime.now(timezone.utc)).get_next(datetime) if p.schedule_cron else None
    s=ConnectorSyncProfile(tenant_id=tenant.id,connector_instance_id=x.id,created_by=user.id,next_run_at=next_run,**p.model_dump()); db.add(s); db.commit(); db.refresh(s); return s
@router.get('/sync-profiles')
def sync_profiles(c=Depends(ctx),db:Session=Depends(get_db)):
    tenant,_=c; return db.scalars(select(ConnectorSyncProfile).where(ConnectorSyncProfile.tenant_id==tenant.id).order_by(ConnectorSyncProfile.created_at.desc())).all()
