"""Campaign CRUD, scoring re-runs and task generation."""

from __future__ import annotations

from fastapi import APIRouter, Depends, HTTPException, Query, status
from sqlalchemy import func, select
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import Session

from app.database import get_db
from app.models import Campaign, CampaignSettings, Lead, OutreachTask, Reply
from app.models.enums import LeadStatus, TaskStatus
from app.schemas.base import Message
from app.schemas.campaign import (
    CampaignCreate,
    CampaignRead,
    CampaignStats,
    CampaignUpdate,
)
from app.services import outreach_service, scoring_service

router = APIRouter(prefix="/campaigns", tags=["campaigns"])


def get_campaign_or_404(db: Session, campaign_id: int) -> Campaign:
    campaign = db.get(Campaign, campaign_id)
    if campaign is None:
        raise HTTPException(status_code=404, detail=f"Campaign {campaign_id} not found")
    return campaign


def build_stats(db: Session, campaign_id: int) -> CampaignStats:
    def count(model, *conditions) -> int:
        query = select(func.count()).select_from(model)
        for condition in conditions:
            query = query.where(condition)
        return int(db.scalar(query) or 0)

    return CampaignStats(
        total_leads=count(Lead, Lead.campaign_id == campaign_id),
        qualified=count(
            Lead,
            Lead.campaign_id == campaign_id,
            Lead.status.in_([LeadStatus.QUALIFIED.value, LeadStatus.HOT.value]),
        ),
        hot=count(Lead, Lead.campaign_id == campaign_id, Lead.status == LeadStatus.HOT.value),
        rejected=count(Lead, Lead.campaign_id == campaign_id, Lead.is_rejected.is_(True)),
        pending_tasks=count(
            OutreachTask,
            OutreachTask.campaign_id == campaign_id,
            OutreachTask.status == TaskStatus.PENDING.value,
        ),
        completed_tasks=count(
            OutreachTask,
            OutreachTask.campaign_id == campaign_id,
            OutreachTask.status == TaskStatus.COMPLETED.value,
        ),
        replies=count(Reply, Reply.campaign_id == campaign_id),
    )


def serialize(db: Session, campaign: Campaign, with_stats: bool = True) -> CampaignRead:
    payload = CampaignRead.model_validate(campaign)
    if with_stats:
        payload.stats = build_stats(db, campaign.id)
    return payload


@router.get("", response_model=list[CampaignRead])
def list_campaigns(
    db: Session = Depends(get_db),
    status_filter: str | None = Query(default=None, alias="status"),
    include_stats: bool = True,
) -> list[CampaignRead]:
    query = select(Campaign).order_by(Campaign.created_at.desc())
    if status_filter:
        query = query.where(Campaign.status == status_filter)
    return [serialize(db, campaign, include_stats) for campaign in db.scalars(query).all()]


@router.post("", response_model=CampaignRead, status_code=status.HTTP_201_CREATED)
def create_campaign(payload: CampaignCreate, db: Session = Depends(get_db)) -> CampaignRead:
    data = payload.model_dump(exclude={"settings", "seed_default_rules"})
    campaign = Campaign(**data)
    db.add(campaign)
    try:
        db.flush()
    except IntegrityError as exc:
        db.rollback()
        raise HTTPException(
            status_code=409, detail=f"A campaign named '{payload.name}' already exists"
        ) from exc

    settings_payload = payload.settings.model_dump() if payload.settings else {}
    db.add(CampaignSettings(campaign_id=campaign.id, **settings_payload))

    if payload.seed_default_rules:
        from app.services.seed_service import seed_campaign_rules

        seed_campaign_rules(db, campaign)

    db.commit()
    db.refresh(campaign)
    return serialize(db, campaign)


@router.get("/{campaign_id}", response_model=CampaignRead)
def get_campaign(campaign_id: int, db: Session = Depends(get_db)) -> CampaignRead:
    return serialize(db, get_campaign_or_404(db, campaign_id))


@router.put("/{campaign_id}", response_model=CampaignRead)
def update_campaign(
    campaign_id: int, payload: CampaignUpdate, db: Session = Depends(get_db)
) -> CampaignRead:
    campaign = get_campaign_or_404(db, campaign_id)
    data = payload.model_dump(exclude_unset=True, exclude={"settings"})
    for key, value in data.items():
        setattr(campaign, key, value)

    if payload.settings is not None:
        if campaign.settings is None:
            campaign.settings = CampaignSettings(campaign_id=campaign.id)
        for key, value in payload.settings.model_dump().items():
            setattr(campaign.settings, key, value)

    try:
        db.commit()
    except IntegrityError as exc:
        db.rollback()
        raise HTTPException(status_code=409, detail="Campaign name already in use") from exc
    db.refresh(campaign)
    return serialize(db, campaign)


@router.delete("/{campaign_id}", response_model=Message)
def delete_campaign(campaign_id: int, db: Session = Depends(get_db)) -> Message:
    campaign = get_campaign_or_404(db, campaign_id)
    name = campaign.name
    db.delete(campaign)
    db.commit()
    return Message(detail=f"Campaign '{name}' and its prospects were deleted")


@router.post("/{campaign_id}/rescore", response_model=dict)
def rescore_campaign(campaign_id: int, db: Session = Depends(get_db)) -> dict:
    campaign = get_campaign_or_404(db, campaign_id)
    return scoring_service.rescore_campaign(db, campaign)


@router.post("/{campaign_id}/generate-tasks", response_model=dict)
def generate_tasks(
    campaign_id: int, only_qualified: bool = True, db: Session = Depends(get_db)
) -> dict:
    campaign = get_campaign_or_404(db, campaign_id)
    return outreach_service.generate_tasks_for_campaign(db, campaign, only_qualified)
