from datetime import datetime
from typing import Any
from fastapi import APIRouter, Depends, Header, HTTPException, Query
from pydantic import BaseModel, EmailStr, Field
from sqlalchemy import func, or_, select
from sqlalchemy.orm import Session
from app.api.deps import current_user
from app.core.config import settings
from app.core.database import get_db
from app.models.entities import (
    BillingInvoice, CheckoutSession, CommercialLead, CommercialOpportunity,
    CustomerHealthSnapshot, CustomerSuccessPlan, KnowledgeArticleV029,
    NPSSurveyResponse, PaymentGatewayConfig, SaaSOnboardingApplication,
    SaaSPlan, SupportTicket, SupportTicketMessage, Tenant, User, utcnow,
)
from app.services.commercial_platform import (
    activate_trial, calculate_health, create_checkout, create_public_lead,
    gateway_public, save_gateway, sla_due, start_trial_application,
)
from app.services.saas_tenancy import resolve_membership

router=APIRouter(prefix='/api/v029',tags=['V029 Commercial Platform & Customer Success'])
PLATFORM={'admin','director'}
OPS={'admin','director','manager'}

class LeadIn(BaseModel):
    company_name:str=Field(min_length=2,max_length=220); contact_name:str=Field(min_length=2,max_length=180); email:EmailStr; phone:str=''; segment:str=''; notes:str=''; source:str='website'
class TrialIn(BaseModel):
    company_name:str=Field(min_length=2,max_length=220); admin_name:str=Field(min_length=2,max_length=180); admin_email:EmailStr; tax_id:str=''; plan_code:str; billing_cycle:str='monthly'; provider:str='stripe'; trial_days:int=Field(default=14,ge=0,le=60)
class ActivateIn(BaseModel): token:str=Field(min_length=20); password:str=Field(min_length=12,max_length=200)
class GatewayIn(BaseModel): provider:str; name:str; environment:str='sandbox'; config:dict[str,Any]=Field(default_factory=dict); webhook_secret:str=''; enabled:bool=False
class OpportunityIn(BaseModel): lead_id:str|None=None; name:str; stage:str='qualification'; plan_code:str=''; amount_cents:int=0; probability:int=10; expected_close_at:datetime|None=None
class TicketIn(BaseModel): subject:str=Field(min_length=4,max_length=250); description:str=Field(min_length=4); category:str='general'; priority:str='normal'
class TicketMessageIn(BaseModel): body:str=Field(min_length=1); internal:bool=False; attachments:list=Field(default_factory=list)
class TicketUpdateIn(BaseModel): status:str|None=None; priority:str|None=None; assigned_to:str|None=None
class ArticleIn(BaseModel): title:str; slug:str; category:str='general'; body:str; status:str='draft'; public:bool=False; tags:list=Field(default_factory=list)
class SuccessPlanIn(BaseModel): owner_id:str|None=None; objectives:list=Field(default_factory=list); milestones:list=Field(default_factory=list); risks:list=Field(default_factory=list); next_review_at:datetime|None=None
class NPSIn(BaseModel): score:int=Field(ge=0,le=10); comment:str=''

def platform_user(user:User=Depends(current_user)):
    if getattr(user.role,'value',user.role) not in PLATFORM: raise HTTPException(403,'Administração global necessária')
    return user

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

def tenant_admin(ctx=Depends(tenant_ctx)):
    tenant,user,membership=ctx; role=getattr(user.role,'value',user.role)
    if role not in PLATFORM and getattr(membership,'tenant_role','') not in ('owner','admin'): raise HTTPException(403,'Administração do tenant necessária')
    return ctx

@router.get('/public/plans')
def public_plans(db:Session=Depends(get_db)):
    plans=db.scalars(select(SaaSPlan).where(SaaSPlan.enabled==True).order_by(SaaSPlan.monthly_price_cents)).all()
    return [{'code':p.code,'name':p.name,'currency':p.currency,'monthly_price_cents':p.monthly_price_cents,'annual_price_cents':p.annual_price_cents,'limits':p.limits_json} for p in plans]

@router.post('/public/leads',status_code=201)
def public_lead(p:LeadIn,db:Session=Depends(get_db)):
    lead=create_public_lead(db,p.model_dump()); return {'id':lead.id,'status':lead.status}

@router.post('/public/trial',status_code=201)
def public_trial(p:TrialIn,db:Session=Depends(get_db)):
    if p.billing_cycle not in ('monthly','annual'): raise HTTPException(400,'Ciclo inválido')
    try: app,token=start_trial_application(db,p.model_dump())
    except ValueError as exc: raise HTTPException(409,str(exc))
    checkout=create_checkout(db,app,p.provider,p.billing_cycle,p.trial_days)
    result={'application_id':app.id,'checkout_session_id':checkout.id,'checkout_url':checkout.checkout_url,'status':checkout.status,'activation_expires_at':app.activation_expires_at}
    if settings.app_env!='production': result['activation_token']=token
    return result

@router.post('/public/trial/{application_id}/activate')
def activate(application_id:str,p:ActivateIn,db:Session=Depends(get_db)):
    app=db.get(SaaSOnboardingApplication,application_id)
    if not app: raise HTTPException(404,'Solicitação não encontrada')
    try: tenant=activate_trial(db,app,p.token,p.password)
    except ValueError as exc: raise HTTPException(400,str(exc))
    return {'tenant_id':tenant.id,'slug':tenant.slug,'status':tenant.status,'login_email':app.admin_email}

@router.get('/summary')
def summary(_:User=Depends(platform_user),db:Session=Depends(get_db)):
    return {'version':'V029','leads':db.scalar(select(func.count()).select_from(CommercialLead)) or 0,'opportunities':db.scalar(select(func.count()).select_from(CommercialOpportunity)) or 0,'open_tickets':db.scalar(select(func.count()).select_from(SupportTicket).where(SupportTicket.status!='resolved')) or 0,'trials':db.scalar(select(func.count()).select_from(SaaSOnboardingApplication).where(SaaSOnboardingApplication.status.in_(['pending','activated']))) or 0}

@router.post('/gateways')
def gateways_create(p:GatewayIn,user:User=Depends(platform_user),db:Session=Depends(get_db)):
    if p.provider not in ('stripe','getnet','asaas','mercadopago','pagarme','mock'): raise HTTPException(400,'Gateway não suportado')
    return gateway_public(save_gateway(db,p.provider,p.name,p.environment,p.config,p.webhook_secret,user.id,p.enabled))

@router.get('/gateways')
def gateways(_:User=Depends(platform_user),db:Session=Depends(get_db)):
    return [gateway_public(x) for x in db.scalars(select(PaymentGatewayConfig).order_by(PaymentGatewayConfig.provider)).all()]

@router.get('/leads')
def leads(_:User=Depends(platform_user),db:Session=Depends(get_db),status:str|None=None):
    q=select(CommercialLead).order_by(CommercialLead.created_at.desc()); q=q.where(CommercialLead.status==status) if status else q
    return db.scalars(q.limit(500)).all()

@router.post('/opportunities')
def opportunity(p:OpportunityIn,user:User=Depends(platform_user),db:Session=Depends(get_db)):
    item=CommercialOpportunity(**p.model_dump(),owner_id=user.id); db.add(item); db.commit(); db.refresh(item); return item

@router.get('/opportunities')
def opportunities(_:User=Depends(platform_user),db:Session=Depends(get_db)):
    return db.scalars(select(CommercialOpportunity).order_by(CommercialOpportunity.updated_at.desc()).limit(500)).all()

@router.get('/portal/financial')
def financial(ctx=Depends(tenant_ctx),db:Session=Depends(get_db)):
    tenant,_,_=ctx
    invoices=db.scalars(select(BillingInvoice).where(BillingInvoice.tenant_id==tenant.id).order_by(BillingInvoice.created_at.desc()).limit(100)).all()
    return {'tenant_id':tenant.id,'invoices':invoices}

@router.post('/tickets',status_code=201)
def create_ticket(p:TicketIn,ctx=Depends(tenant_ctx),db:Session=Depends(get_db)):
    tenant,user,_=ctx; item=SupportTicket(tenant_id=tenant.id,requester_id=user.id,subject=p.subject,description=p.description,category=p.category,priority=p.priority,sla_due_at=sla_due(p.priority))
    db.add(item); db.commit(); db.refresh(item); return item

@router.get('/tickets')
def tickets(ctx=Depends(tenant_ctx),db:Session=Depends(get_db),status:str|None=None):
    tenant,user,membership=ctx; role=getattr(user.role,'value',user.role); q=select(SupportTicket).where(SupportTicket.tenant_id==tenant.id)
    if role not in OPS and getattr(membership,'tenant_role','') not in ('owner','admin'): q=q.where(SupportTicket.requester_id==user.id)
    if status: q=q.where(SupportTicket.status==status)
    return db.scalars(q.order_by(SupportTicket.updated_at.desc()).limit(500)).all()

@router.patch('/tickets/{ticket_id}')
def update_ticket(ticket_id:str,p:TicketUpdateIn,ctx=Depends(tenant_admin),db:Session=Depends(get_db)):
    tenant,user,_=ctx; item=db.scalar(select(SupportTicket).where(SupportTicket.id==ticket_id,SupportTicket.tenant_id==tenant.id))
    if not item: raise HTTPException(404,'Chamado não encontrado')
    for key,val in p.model_dump(exclude_none=True).items(): setattr(item,key,val)
    if p.status=='resolved': item.resolved_at=utcnow()
    db.add(item); db.commit(); db.refresh(item); return item

@router.post('/tickets/{ticket_id}/messages')
def ticket_message(ticket_id:str,p:TicketMessageIn,ctx=Depends(tenant_ctx),db:Session=Depends(get_db)):
    tenant,user,membership=ctx; ticket=db.scalar(select(SupportTicket).where(SupportTicket.id==ticket_id,SupportTicket.tenant_id==tenant.id))
    if not ticket: raise HTTPException(404,'Chamado não encontrado')
    role=getattr(user.role,'value',user.role)
    if ticket.requester_id!=user.id and role not in OPS and getattr(membership,'tenant_role','') not in ('owner','admin'): raise HTTPException(403,'Acesso negado')
    if p.internal and role not in OPS: raise HTTPException(403,'Nota interna restrita')
    msg=SupportTicketMessage(tenant_id=tenant.id,ticket_id=ticket.id,author_id=user.id,body=p.body,internal=p.internal,attachments=p.attachments)
    if not ticket.first_response_at and user.id!=ticket.requester_id: ticket.first_response_at=utcnow()
    db.add_all([msg,ticket]); db.commit(); db.refresh(msg); return msg

@router.get('/tickets/{ticket_id}/messages')
def ticket_messages(ticket_id:str,ctx=Depends(tenant_ctx),db:Session=Depends(get_db)):
    tenant,user,membership=ctx; ticket=db.scalar(select(SupportTicket).where(SupportTicket.id==ticket_id,SupportTicket.tenant_id==tenant.id))
    if not ticket: raise HTTPException(404,'Chamado não encontrado')
    role=getattr(user.role,'value',user.role); is_staff=role in OPS or getattr(membership,'tenant_role','') in ('owner','admin')
    q=select(SupportTicketMessage).where(SupportTicketMessage.ticket_id==ticket.id,SupportTicketMessage.tenant_id==tenant.id)
    if not is_staff: q=q.where(SupportTicketMessage.internal==False)
    return db.scalars(q.order_by(SupportTicketMessage.created_at)).all()

@router.post('/knowledge')
def article_create(p:ArticleIn,ctx=Depends(tenant_admin),db:Session=Depends(get_db)):
    tenant,user,_=ctx; item=KnowledgeArticleV029(tenant_id=tenant.id,created_by=user.id,**p.model_dump()); db.add(item); db.commit(); db.refresh(item); return item

@router.get('/knowledge')
def articles(ctx=Depends(tenant_ctx),db:Session=Depends(get_db),q:str=''):
    tenant,_,_=ctx; stmt=select(KnowledgeArticleV029).where(or_(KnowledgeArticleV029.tenant_id==tenant.id,KnowledgeArticleV029.tenant_id==None),KnowledgeArticleV029.status=='published')
    if q: stmt=stmt.where(or_(KnowledgeArticleV029.title.ilike(f'%{q}%'),KnowledgeArticleV029.body.ilike(f'%{q}%')))
    return db.scalars(stmt.order_by(KnowledgeArticleV029.updated_at.desc()).limit(100)).all()

@router.post('/customer-success/health/recalculate')
def health_recalculate(ctx=Depends(tenant_admin),db:Session=Depends(get_db)):
    tenant,_,_=ctx; return calculate_health(db,tenant.id)

@router.get('/customer-success/health')
def health(ctx=Depends(tenant_ctx),db:Session=Depends(get_db)):
    tenant,_,_=ctx; return db.scalar(select(CustomerHealthSnapshot).where(CustomerHealthSnapshot.tenant_id==tenant.id).order_by(CustomerHealthSnapshot.calculated_at.desc()))

@router.post('/customer-success/plans')
def success_plan(p:SuccessPlanIn,ctx=Depends(tenant_admin),db:Session=Depends(get_db)):
    tenant,_,_=ctx; item=CustomerSuccessPlan(tenant_id=tenant.id,**p.model_dump()); db.add(item); db.commit(); db.refresh(item); return item

@router.post('/nps')
def nps(p:NPSIn,ctx=Depends(tenant_ctx),db:Session=Depends(get_db)):
    tenant,user,_=ctx; item=NPSSurveyResponse(tenant_id=tenant.id,user_id=user.id,score=p.score,comment=p.comment); db.add(item); db.commit(); db.refresh(item); return item
