from decimal import Decimal

from sqlalchemy import and_, func
from sqlalchemy.orm import Session

from ..accounts.models import Account
from ..activities.models import Activity
from ..contacts.models import Contact
from ..leads.models import Lead
from ..opportunities.models import Opportunity
from ..pipelines.models import PipelineStage


def _count(db: Session, model, tenant_id: int, company_id: int, *extra) -> int:
    query = db.query(func.count(model.id)).filter(
        model.tenant_id == tenant_id,
        model.company_id == company_id,
        model.deleted_at.is_(None),
        *extra,
    )
    return int(query.scalar() or 0)


def _sum_amount(db: Session, tenant_id: int, company_id: int, *extra) -> Decimal:
    total = (
        db.query(func.coalesce(func.sum(Opportunity.amount), 0))
        .filter(
            Opportunity.tenant_id == tenant_id,
            Opportunity.company_id == company_id,
            Opportunity.deleted_at.is_(None),
            *extra,
        )
        .scalar()
    )
    return Decimal(total or 0)


def get_metrics(db: Session, tenant_id: int, company_id: int, pipeline_id: int | None = None) -> dict:
    """KPIs y embudo por etapa para el dashboard del CRM."""
    open_opps = _count(db, Opportunity, tenant_id, company_id, Opportunity.status == "open")
    won_opps = _count(db, Opportunity, tenant_id, company_id, Opportunity.status == "won")

    # Embudo: oportunidades abiertas agrupadas por etapa
    stage_filters = [
        PipelineStage.tenant_id == tenant_id,
        PipelineStage.company_id == company_id,
        PipelineStage.deleted_at.is_(None),
    ]
    if pipeline_id is not None:
        stage_filters.append(PipelineStage.pipeline_id == pipeline_id)

    rows = (
        db.query(
            PipelineStage.id,
            PipelineStage.name,
            PipelineStage.position,
            func.count(Opportunity.id),
            func.coalesce(func.sum(Opportunity.amount), 0),
        )
        .outerjoin(
            Opportunity,
            and_(
                Opportunity.stage_id == PipelineStage.id,
                Opportunity.status == "open",
                Opportunity.deleted_at.is_(None),
                Opportunity.tenant_id == tenant_id,
                Opportunity.company_id == company_id,
            ),
        )
        .filter(*stage_filters)
        .group_by(PipelineStage.id, PipelineStage.name, PipelineStage.position)
        .order_by(PipelineStage.position.asc())
        .all()
    )

    by_stage = [
        {
            "stage_id": row[0],
            "stage_name": row[1],
            "position": row[2],
            "count": int(row[3] or 0),
            "value": Decimal(row[4] or 0),
        }
        for row in rows
    ]

    return {
        "total_accounts": _count(db, Account, tenant_id, company_id),
        "total_contacts": _count(db, Contact, tenant_id, company_id),
        "total_leads": _count(db, Lead, tenant_id, company_id),
        "open_leads": _count(db, Lead, tenant_id, company_id, Lead.status != "converted"),
        "open_opportunities": open_opps,
        "open_pipeline_value": _sum_amount(db, tenant_id, company_id, Opportunity.status == "open"),
        "won_opportunities": won_opps,
        "won_value": _sum_amount(db, tenant_id, company_id, Opportunity.status == "won"),
        "pending_activities": _count(db, Activity, tenant_id, company_id, Activity.status == "pending"),
        "by_stage": by_stage,
    }
