from typing import Any
from fastapi import APIRouter, Depends, Header, HTTPException, Query
from pydantic import BaseModel, EmailStr, 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 (
    MarketplaceAsset, MarketplaceAssetVersion, MarketplacePublisher,
    MarketplaceReview, PartnerCommission, PartnerReferral,
    Tenant, TenantMarketplaceInstallation, User, utcnow,
)
from app.services.marketplace import (
    ALLOWED_ASSET_TYPES, asset_public, install_asset, publisher_public,
    sha256_file, validate_code, validate_manifest,
)
from app.services.saas_tenancy import resolve_membership

router=APIRouter(prefix='/api/v030',tags=['V030 Marketplace & Partner Ecosystem'])
PLATFORM={'admin','director'}

class PublisherIn(BaseModel):
    legal_name:str=Field(min_length=2,max_length=220); trade_name:str=''; tax_id:str=''; website:str=''; support_email:EmailStr; publisher_type:str='partner'; revenue_share_bps:int=Field(default=2000,ge=0,le=8000)
class AssetIn(BaseModel):
    publisher_id:str; code:str; asset_type:str; name:str; short_description:str=''; description:str=''; category:str='general'; tags:list=Field(default_factory=list); icon_url:str=''; visibility:str='public'; pricing_model:str='free'; monthly_price_cents:int=0; one_time_price_cents:int=0; currency:str='BRL'; trial_days:int=Field(default=0,ge=0,le=90); required_plan_codes:list=Field(default_factory=list); required_module_codes:list=Field(default_factory=list)
class VersionIn(BaseModel):
    version:str; min_platform_version:str='0.30.0'; max_platform_version:str=''; manifest:dict[str,Any]; dependencies:list=Field(default_factory=list); package_path:str=''; package_sha256:str=''; signature:str=''; changelog:str=''
class InstallIn(BaseModel): version_id:str; configuration:dict[str,Any]=Field(default_factory=dict)
class ReviewIn(BaseModel): rating:int=Field(ge=1,le=5); title:str=''; comment:str=''
class ReferralIn(BaseModel): publisher_id:str; lead_id:str|None=None; tenant_id:str|None=None; notes: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 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('/summary')
def summary(_:User=Depends(platform_user),db:Session=Depends(get_db)):
    return {'version':'V030','publishers':db.scalar(select(func.count()).select_from(MarketplacePublisher)) or 0,'published_assets':db.scalar(select(func.count()).select_from(MarketplaceAsset).where(MarketplaceAsset.status=='published')) or 0,'installations':db.scalar(select(func.count()).select_from(TenantMarketplaceInstallation)) or 0,'pending_commissions_cents':db.scalar(select(func.coalesce(func.sum(PartnerCommission.commission_amount_cents),0)).where(PartnerCommission.status=='pending')) or 0}

@router.post('/publishers',status_code=201)
def create_publisher(p:PublisherIn,user:User=Depends(current_user),db:Session=Depends(get_db)):
    item=MarketplacePublisher(**p.model_dump(),created_by=user.id,status='pending'); db.add(item); db.commit(); db.refresh(item); return publisher_public(item)

@router.get('/publishers')
def list_publishers(_:User=Depends(platform_user),db:Session=Depends(get_db),status:str|None=None):
    q=select(MarketplacePublisher).order_by(MarketplacePublisher.created_at.desc()); q=q.where(MarketplacePublisher.status==status) if status else q
    return [publisher_public(x) for x in db.scalars(q.limit(500)).all()]

@router.post('/publishers/{publisher_id}/approve')
def approve_publisher(publisher_id:str,user:User=Depends(platform_user),db:Session=Depends(get_db)):
    item=db.get(MarketplacePublisher,publisher_id)
    if not item: raise HTTPException(404,'Publicador não encontrado')
    item.status='approved'; item.approved_by=user.id; item.approved_at=utcnow(); db.add(item); db.commit(); return publisher_public(item)

@router.post('/assets',status_code=201)
def create_asset(p:AssetIn,user:User=Depends(current_user),db:Session=Depends(get_db)):
    pub=db.get(MarketplacePublisher,p.publisher_id)
    if not pub or pub.status!='approved': raise HTTPException(400,'Publicador não aprovado')
    if p.asset_type not in ALLOWED_ASSET_TYPES: raise HTTPException(400,'Tipo de ativo inválido')
    try: code=validate_code(p.code)
    except ValueError as exc: raise HTTPException(400,str(exc))
    if db.scalar(select(MarketplaceAsset).where(MarketplaceAsset.code==code)): raise HTTPException(409,'Código já utilizado')
    data=p.model_dump(); data['code']=code; item=MarketplaceAsset(**data,created_by=user.id,status='draft'); db.add(item); db.commit(); db.refresh(item); return asset_public(item)

@router.post('/assets/{asset_id}/versions',status_code=201)
def create_version(asset_id:str,p:VersionIn,user:User=Depends(current_user),db:Session=Depends(get_db)):
    asset=db.get(MarketplaceAsset,asset_id)
    if not asset: raise HTTPException(404,'Ativo não encontrado')
    try: validate_manifest(p.manifest)
    except ValueError as exc: raise HTTPException(400,str(exc))
    checksum=p.package_sha256
    if p.package_path:
        try: checksum=sha256_file(p.package_path)
        except OSError as exc: raise HTTPException(400,f'Pacote não localizado: {exc}')
    item=MarketplaceAssetVersion(asset_id=asset.id,created_by=user.id,**{**p.model_dump(),'package_sha256':checksum},status='draft',security_review_status='pending'); db.add(item); db.commit(); db.refresh(item); return item

@router.post('/assets/{asset_id}/publish')
def publish_asset(asset_id:str,version_id:str=Query(...),user:User=Depends(platform_user),db:Session=Depends(get_db)):
    asset=db.get(MarketplaceAsset,asset_id); version=db.get(MarketplaceAssetVersion,version_id)
    if not asset or not version or version.asset_id!=asset.id: raise HTTPException(404,'Ativo ou versão não encontrado')
    if not version.package_sha256 and version.manifest.get('requires_package',False): raise HTTPException(400,'Checksum obrigatório')
    version.security_review_status='approved'; version.status='published'; asset.status='published'; db.add_all([asset,version]); db.commit(); return asset_public(asset,version)

@router.get('/catalog')
def catalog(db:Session=Depends(get_db),asset_type:str|None=None,q:str=''):
    stmt=select(MarketplaceAsset).where(MarketplaceAsset.status=='published',MarketplaceAsset.visibility=='public')
    if asset_type: stmt=stmt.where(MarketplaceAsset.asset_type==asset_type)
    if q: stmt=stmt.where(MarketplaceAsset.name.ilike(f'%{q}%'))
    assets=db.scalars(stmt.order_by(MarketplaceAsset.name).limit(500)).all(); result=[]
    for a in assets:
        v=db.scalar(select(MarketplaceAssetVersion).where(MarketplaceAssetVersion.asset_id==a.id,MarketplaceAssetVersion.status=='published').order_by(MarketplaceAssetVersion.created_at.desc()))
        result.append(asset_public(a,v))
    return result

@router.post('/assets/{asset_id}/install')
def install(asset_id:str,p:InstallIn,ctx=Depends(tenant_admin),db:Session=Depends(get_db)):
    tenant,user,_=ctx; asset=db.get(MarketplaceAsset,asset_id); version=db.get(MarketplaceAssetVersion,p.version_id)
    if not asset or not version or version.asset_id!=asset.id: raise HTTPException(404,'Ativo ou versão não encontrado')
    try: item=install_asset(db,tenant.id,user.id,asset,version,configuration=p.configuration)
    except ValueError as exc: raise HTTPException(400,str(exc))
    return item

@router.get('/installations')
def installations(ctx=Depends(tenant_ctx),db:Session=Depends(get_db)):
    tenant,_,_=ctx
    return db.scalars(select(TenantMarketplaceInstallation).where(TenantMarketplaceInstallation.tenant_id==tenant.id).order_by(TenantMarketplaceInstallation.installed_at.desc())).all()

@router.patch('/installations/{installation_id}')
def update_installation(installation_id:str,enabled:bool,ctx=Depends(tenant_admin),db:Session=Depends(get_db)):
    tenant,_,_=ctx; item=db.scalar(select(TenantMarketplaceInstallation).where(TenantMarketplaceInstallation.id==installation_id,TenantMarketplaceInstallation.tenant_id==tenant.id))
    if not item: raise HTTPException(404,'Instalação não encontrada')
    item.enabled=enabled; item.status='installed' if enabled else 'disabled'; db.add(item); db.commit(); return item

@router.post('/assets/{asset_id}/reviews')
def review(asset_id:str,p:ReviewIn,ctx=Depends(tenant_ctx),db:Session=Depends(get_db)):
    tenant,user,_=ctx
    installed=db.scalar(select(TenantMarketplaceInstallation).where(TenantMarketplaceInstallation.tenant_id==tenant.id,TenantMarketplaceInstallation.asset_id==asset_id))
    if not installed: raise HTTPException(403,'Somente clientes que instalaram podem avaliar')
    item=db.scalar(select(MarketplaceReview).where(MarketplaceReview.tenant_id==tenant.id,MarketplaceReview.asset_id==asset_id,MarketplaceReview.user_id==user.id))
    if item: item.rating=p.rating; item.title=p.title; item.comment=p.comment
    else: item=MarketplaceReview(tenant_id=tenant.id,asset_id=asset_id,user_id=user.id,**p.model_dump()); db.add(item)
    db.commit(); db.refresh(item); return item

@router.post('/referrals')
def referral(p:ReferralIn,user:User=Depends(current_user),db:Session=Depends(get_db)):
    pub=db.get(MarketplacePublisher,p.publisher_id)
    if not pub or pub.status!='approved': raise HTTPException(404,'Parceiro aprovado não encontrado')
    import secrets
    item=PartnerReferral(**p.model_dump(),referral_code=secrets.token_urlsafe(18),created_by=user.id); db.add(item); db.commit(); db.refresh(item); return item

@router.get('/partner/commissions')
def commissions(publisher_id:str,user:User=Depends(current_user),db:Session=Depends(get_db)):
    pub=db.get(MarketplacePublisher,publisher_id)
    if not pub: raise HTTPException(404,'Parceiro não encontrado')
    role=getattr(user.role,'value',user.role)
    if role not in PLATFORM and pub.created_by!=user.id: raise HTTPException(403,'Acesso negado')
    return db.scalars(select(PartnerCommission).where(PartnerCommission.publisher_id==publisher_id).order_by(PartnerCommission.created_at.desc()).limit(1000)).all()
