from datetime import datetime, timezone

from fastapi import HTTPException, status
from sqlalchemy.orm import Session

from .dto import PipelineCreate, PipelineUpdate, StageCreate, StageUpdate
from .models import Pipeline, PipelineStage


# ----- Pipelines -----

def get_pipelines(db: Session, tenant_id: int, company_id: int) -> list[Pipeline]:
    return (
        db.query(Pipeline)
        .filter(
            Pipeline.tenant_id == tenant_id,
            Pipeline.company_id == company_id,
            Pipeline.deleted_at.is_(None),
        )
        .order_by(Pipeline.is_default.desc(), Pipeline.name.asc())
        .all()
    )


def get_pipeline(db: Session, pipeline_id: int, tenant_id: int, company_id: int) -> Pipeline:
    pipeline = (
        db.query(Pipeline)
        .filter(
            Pipeline.id == pipeline_id,
            Pipeline.tenant_id == tenant_id,
            Pipeline.company_id == company_id,
            Pipeline.deleted_at.is_(None),
        )
        .first()
    )
    if not pipeline:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Embudo no encontrado")
    return pipeline


def _clear_default(db: Session, tenant_id: int, company_id: int) -> None:
    """Solo un embudo puede ser el predeterminado por company."""
    db.query(Pipeline).filter(
        Pipeline.tenant_id == tenant_id,
        Pipeline.company_id == company_id,
        Pipeline.is_default.is_(True),
    ).update({Pipeline.is_default: False})


def create_pipeline(db: Session, payload: PipelineCreate, tenant_id: int, company_id: int) -> Pipeline:
    if payload.is_default:
        _clear_default(db, tenant_id, company_id)
    pipeline = Pipeline(**payload.model_dump(), tenant_id=tenant_id, company_id=company_id)
    db.add(pipeline)
    db.commit()
    db.refresh(pipeline)
    return pipeline


def update_pipeline(
    db: Session, pipeline_id: int, payload: PipelineUpdate, tenant_id: int, company_id: int
) -> Pipeline:
    pipeline = get_pipeline(db, pipeline_id, tenant_id, company_id)
    data = payload.model_dump(exclude_unset=True)
    if data.get("is_default"):
        _clear_default(db, tenant_id, company_id)
    for field, value in data.items():
        setattr(pipeline, field, value)
    db.commit()
    db.refresh(pipeline)
    return pipeline


def delete_pipeline(db: Session, pipeline_id: int, tenant_id: int, company_id: int) -> None:
    pipeline = get_pipeline(db, pipeline_id, tenant_id, company_id)
    pipeline.deleted_at = datetime.now(timezone.utc)
    db.commit()


# ----- Stages -----

def get_stages(db: Session, tenant_id: int, company_id: int, pipeline_id: int | None = None) -> list[PipelineStage]:
    query = db.query(PipelineStage).filter(
        PipelineStage.tenant_id == tenant_id,
        PipelineStage.company_id == company_id,
        PipelineStage.deleted_at.is_(None),
    )
    if pipeline_id is not None:
        query = query.filter(PipelineStage.pipeline_id == pipeline_id)
    return query.order_by(PipelineStage.position.asc()).all()


def get_stage(db: Session, stage_id: int, tenant_id: int, company_id: int) -> PipelineStage:
    stage = (
        db.query(PipelineStage)
        .filter(
            PipelineStage.id == stage_id,
            PipelineStage.tenant_id == tenant_id,
            PipelineStage.company_id == company_id,
            PipelineStage.deleted_at.is_(None),
        )
        .first()
    )
    if not stage:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Etapa no encontrada")
    return stage


def create_stage(db: Session, payload: StageCreate, tenant_id: int, company_id: int) -> PipelineStage:
    # La etapa debe pertenecer a un embudo del mismo tenant/company
    get_pipeline(db, payload.pipeline_id, tenant_id, company_id)
    stage = PipelineStage(**payload.model_dump(), tenant_id=tenant_id, company_id=company_id)
    db.add(stage)
    db.commit()
    db.refresh(stage)
    return stage


def update_stage(
    db: Session, stage_id: int, payload: StageUpdate, tenant_id: int, company_id: int
) -> PipelineStage:
    stage = get_stage(db, stage_id, tenant_id, company_id)
    for field, value in payload.model_dump(exclude_unset=True).items():
        setattr(stage, field, value)
    db.commit()
    db.refresh(stage)
    return stage


def delete_stage(db: Session, stage_id: int, tenant_id: int, company_id: int) -> None:
    stage = get_stage(db, stage_id, tenant_id, company_id)
    stage.deleted_at = datetime.now(timezone.utc)
    db.commit()
