"""Rule-based message generation. No LLM, no external API, fully deterministic."""

from __future__ import annotations

from dataclasses import dataclass
from typing import Any

from sqlalchemy import select
from sqlalchemy.orm import Session

from app.models import Campaign, Lead, MessageTemplate, User
from app.models.enums import TaskType
from app.rules.engine import EvaluationResult
from app.services import template_engine
from app.services.scoring_service import evaluate_lead

#: LinkedIn caps connection-request notes at 300 characters.
CONNECTION_NOTE_LIMIT = 300

#: Which template category serves which outreach step.
TASK_CATEGORY: dict[str, str] = {
    TaskType.REVIEW_CONNECTION: "connection",
    TaskType.CONNECTION_REQUEST: "connection",
    TaskType.FIRST_MESSAGE: "first_message",
    TaskType.FOLLOW_UP_1: "follow_up_1",
    TaskType.FOLLOW_UP_2: "follow_up_2",
    TaskType.FOLLOW_UP_3: "follow_up_3",
}

TEMPLATE_CATEGORIES: list[str] = [
    "connection",
    "first_message",
    "follow_up_1",
    "follow_up_2",
    "follow_up_3",
]


@dataclass(slots=True)
class GeneratedMessage:
    message: str
    template_id: int | None
    template_name: str | None
    variant_name: str | None
    category: str
    phrases: list[str]
    reasons: list[dict[str, Any]]
    truncated: bool
    length: int
    warnings: list[str]

    def as_dict(self) -> dict[str, Any]:
        return {
            "message": self.message,
            "templateId": self.template_id,
            "templateName": self.template_name,
            "variantName": self.variant_name,
            "category": self.category,
            "phrases": self.phrases,
            "reasons": self.reasons,
            "truncated": self.truncated,
            "length": self.length,
            "warnings": self.warnings,
        }


def get_sender(db: Session) -> User | None:
    return db.scalars(select(User).order_by(User.id)).first()


def load_templates(db: Session, campaign: Campaign | None, category: str) -> list[MessageTemplate]:
    """Campaign templates first, then shared/global templates, both filtered by category."""
    query = select(MessageTemplate).where(
        MessageTemplate.is_active.is_(True), MessageTemplate.category == category
    )
    if campaign is not None:
        query = query.where(
            (MessageTemplate.campaign_id == campaign.id)
            | (MessageTemplate.campaign_id.is_(None))
        )
    else:
        query = query.where(MessageTemplate.campaign_id.is_(None))

    templates = list(db.scalars(query).all())
    # Campaign-specific templates outrank shared ones with the same priority.
    templates.sort(key=lambda t: (0 if t.campaign_id else 1, t.priority, t.id))
    return templates


def generate_message(
    db: Session,
    lead: Lead,
    campaign: Campaign | None = None,
    task_type: str = TaskType.CONNECTION_REQUEST,
    evaluation: EvaluationResult | None = None,
    template_id: int | None = None,
    enforce_limit: bool | None = None,
) -> GeneratedMessage:
    """Build the outreach message for one lead using templates + rules only."""
    campaign = campaign or lead.campaign
    category = TASK_CATEGORY.get(task_type, "connection")
    warnings: list[str] = []

    evaluation = evaluation or evaluate_lead(db, lead, campaign)
    context = template_engine.build_context(lead, sender=get_sender(db), campaign=campaign)

    template: MessageTemplate | None = None
    if template_id is not None:
        template = db.get(MessageTemplate, template_id)
        if template is None:
            warnings.append(f"Template {template_id} not found, falling back to rule selection.")

    if template is None:
        candidates = load_templates(db, campaign, category)
        template = template_engine.select_template(
            candidates,
            context,
            preferred_keys=evaluation.template_keys,
        )
        if template is None and campaign is not None and campaign.default_template_id:
            template = db.get(MessageTemplate, campaign.default_template_id)

    variant_name: str | None = None
    if template is not None:
        body = template.body
        seed = lead.profile_url or lead.dedupe_key or f"{lead.full_name}|{lead.company}"
        variant = template_engine.select_variant(template, seed)
        if variant is not None:
            body = variant.body
            variant_name = variant.name
    else:
        body = template_engine.FALLBACK_TEMPLATE_BODY
        warnings.append("No template matched; used the built-in fallback template.")

    try:
        message = template_engine.render_template(body, context)
    except template_engine.TemplateError as exc:
        warnings.append(f"Template error: {exc}. Used the built-in fallback template.")
        message = template_engine.render_template(template_engine.FALLBACK_TEMPLATE_BODY, context)

    message = template_engine.apply_phrases(message, evaluation.phrases)

    should_limit = enforce_limit if enforce_limit is not None else category == "connection"
    truncated = False
    if should_limit and len(message) > CONNECTION_NOTE_LIMIT:
        message = message[: CONNECTION_NOTE_LIMIT - 1].rstrip() + "…"
        truncated = True
        warnings.append(
            f"Message trimmed to LinkedIn's {CONNECTION_NOTE_LIMIT} character connection-note limit."
        )

    return GeneratedMessage(
        message=message,
        template_id=template.id if template else None,
        template_name=template.name if template else None,
        variant_name=variant_name,
        category=category,
        phrases=list(evaluation.phrases),
        reasons=list(evaluation.reasons),
        truncated=truncated,
        length=len(message),
        warnings=warnings,
    )


def preview_template(
    db: Session, body: str, lead: Lead | None = None, campaign: Campaign | None = None
) -> str:
    """Render an arbitrary template body, used by the live editor preview."""
    if lead is not None:
        context = template_engine.build_context(lead, sender=get_sender(db), campaign=campaign)
    else:
        context = template_engine.build_context(
            {
                "first_name": "John",
                "last_name": "Smith",
                "full_name": "John Smith",
                "job_title": "CEO",
                "company": "ABC Realty",
                "industry": "Real Estate",
                "location": "Austin, United States",
                "company_size": 42,
                "headline": "CEO at ABC Realty | Property investment",
                "website": "https://abcrealty.example.com",
            },
            sender=get_sender(db),
            campaign=campaign,
        )
    return template_engine.render_template(body, context)
