# app/modules/prospecting/services/campaign_service.py
"""
Campaign management service.

Handles campaign template CRUD, rendering with prospect data,
and send tracking.
"""

import json
import logging
from datetime import UTC, datetime

from sqlalchemy.orm import Session

from app.modules.prospecting.exceptions import (
    CampaignRenderException,
    CampaignTemplateNotFoundException,
)
from app.modules.prospecting.models import (
    CampaignSend,
    CampaignSendStatus,
    CampaignTemplate,
    Prospect,
)
from app.modules.prospecting.services.prospect_service import prospect_service

logger = logging.getLogger(__name__)


class CampaignService:
    """Service for campaign template management and sending."""

    # --- Template CRUD ---

    def get_templates(
        self,
        db: Session,
        *,
        lead_type: str | None = None,
        active_only: bool = False,
    ) -> list[CampaignTemplate]:
        query = db.query(CampaignTemplate)
        if lead_type:
            query = query.filter(CampaignTemplate.lead_type == lead_type)
        if active_only:
            query = query.filter(CampaignTemplate.is_active.is_(True))
        return query.order_by(CampaignTemplate.lead_type, CampaignTemplate.name).all()

    def get_template_by_id(self, db: Session, template_id: int) -> CampaignTemplate:
        template = db.query(CampaignTemplate).filter(CampaignTemplate.id == template_id).first()
        if not template:
            raise CampaignTemplateNotFoundException(str(template_id))
        return template

    def create_template(self, db: Session, data: dict) -> CampaignTemplate:
        template = CampaignTemplate(
            name=data["name"],
            lead_type=data["lead_type"],
            channel=data.get("channel", "email"),
            language=data.get("language", "fr"),
            subject_template=data.get("subject_template"),
            body_template=data["body_template"],
            is_active=data.get("is_active", True),
        )
        db.add(template)
        db.flush()
        return template

    def update_template(self, db: Session, template_id: int, data: dict) -> CampaignTemplate:
        template = self.get_template_by_id(db, template_id)
        for field in ["name", "lead_type", "channel", "language", "subject_template", "body_template", "is_active"]:
            if field in data and data[field] is not None:
                setattr(template, field, data[field])
        db.flush()
        return template

    def delete_template(self, db: Session, template_id: int) -> bool:
        template = self.get_template_by_id(db, template_id)
        db.delete(template)
        db.flush()
        return True

    # --- Rendering ---

    def render_campaign(self, db: Session, template_id: int, prospect_id: int) -> dict:
        """Render a campaign template with prospect data."""
        template = self.get_template_by_id(db, template_id)
        prospect = prospect_service.get_by_id(db, prospect_id)

        placeholders = self._build_placeholders(prospect)

        try:
            rendered_subject = None
            if template.subject_template:
                rendered_subject = template.subject_template.format(**placeholders)
            rendered_body = template.body_template.format(**placeholders)
        except KeyError as e:
            raise CampaignRenderException(template_id, f"Missing placeholder: {e}")

        return {
            "subject": rendered_subject,
            "body": rendered_body,
        }

    # --- Sending ---

    def send_campaign(
        self,
        db: Session,
        template_id: int,
        prospect_ids: list[int],
        sent_by_user_id: int,
    ) -> list[CampaignSend]:
        """Create campaign send records for prospects."""
        template = self.get_template_by_id(db, template_id)
        sends = []

        for pid in prospect_ids:
            prospect = prospect_service.get_by_id(db, pid)
            placeholders = self._build_placeholders(prospect)

            try:
                rendered_subject = None
                if template.subject_template:
                    rendered_subject = template.subject_template.format(**placeholders)
                rendered_body = template.body_template.format(**placeholders)
            except KeyError:
                rendered_body = template.body_template
                rendered_subject = template.subject_template

            send = CampaignSend(
                template_id=template_id,
                prospect_id=pid,
                channel=template.channel,
                rendered_subject=rendered_subject,
                rendered_body=rendered_body,
                status=CampaignSendStatus.SENT,
                sent_at=datetime.now(UTC),
                sent_by_user_id=sent_by_user_id,
            )
            sends.append(send)

        db.add_all(sends)
        db.flush()
        logger.info("Sent campaign %d to %d prospects", template_id, len(prospect_ids))
        return sends

    def get_sends(
        self,
        db: Session,
        *,
        prospect_id: int | None = None,
        template_id: int | None = None,
    ) -> list[CampaignSend]:
        query = db.query(CampaignSend)
        if prospect_id:
            query = query.filter(CampaignSend.prospect_id == prospect_id)
        if template_id:
            query = query.filter(CampaignSend.template_id == template_id)
        return query.order_by(CampaignSend.created_at.desc()).all()

    def _build_placeholders(self, prospect: Prospect) -> dict:
        """Build template placeholder values from prospect data."""
        contacts = prospect.contacts or []
        primary_email = next((c.value for c in contacts if c.contact_type == "email"), "")
        primary_phone = next((c.value for c in contacts if c.contact_type == "phone"), "")

        reason_flags = []
        if prospect.score and prospect.score.reason_flags:
            try:
                reason_flags = json.loads(prospect.score.reason_flags)
            except (json.JSONDecodeError, TypeError):
                pass

        issues_text = ", ".join(f.replace("_", " ") for f in reason_flags)

        return {
            "business_name": prospect.business_name or prospect.domain_name or "",
            "domain": prospect.domain_name or "",
            "score": str(prospect.score.score) if prospect.score else "—",
            "issues": issues_text,
            "primary_email": primary_email,
            "primary_phone": primary_phone,
            "city": prospect.city or "Luxembourg",
        }


campaign_service = CampaignService()
