"""
Notification creation service (module 1.1.11).

This is called from the Celery task (app/workers/tasks.py) after a
successful analysis tied to a crop_plan_id, comparing the new
feasibility label against the plan's previous one. A change triggers
both an in-app Notification row and a best-effort email via
app/services/email_service.py.

Kept as a plain synchronous function (not async) since it's invoked
from the synchronous Celery task context — see sync_db_session usage
elsewhere in tasks.py for the same pattern.
"""

from app.core.logging_config import get_logger
from app.db.models import CropPlan, Notification, User
from app.db.sync_session import sync_db_session
from app.services.email_service import build_feasibility_change_email, send_email

logger = get_logger(__name__)

SEVERITY_FOR_LABEL = {
    "suitable": "info",
    "risky": "warning",
    "not_suitable": "critical",
}


def notify_feasibility_change_if_needed(
    crop_plan_id: str,
    new_feasibility_label: str | None,
) -> None:
    """
    Looks up the plan's previous latest analysis to determine the prior
    label, and creates a notification only if the label actually
    changed (or this is the plan's first analysis) — avoids spamming
    a notification on every re-run if nothing about the outlook changed.
    """
    if not crop_plan_id or not new_feasibility_label:
        return

    try:
        with sync_db_session() as session:
            plan = session.get(CropPlan, crop_plan_id)
            if plan is None:
                return

            user = session.get(User, plan.user_id)
            if user is None:
                return

            # Find the previous analysis for this plan (excluding the one
            # that was just persisted, which is the most recent row) to
            # determine whether the label actually changed.
            from sqlalchemy import desc, select

            from app.db.models import AnalysisRecord

            previous_records = (
                session.execute(
                    select(AnalysisRecord)
                    .where(AnalysisRecord.crop_plan_id == crop_plan_id)
                    .order_by(desc(AnalysisRecord.created_at))
                    .limit(2)
                )
                .scalars()
                .all()
            )

            old_label = previous_records[1].feasibility_label if len(previous_records) > 1 else None

            is_first_analysis = len(previous_records) <= 1
            label_changed = old_label is not None and old_label != new_feasibility_label

            if not is_first_analysis and not label_changed:
                return  # nothing meaningfully new to tell the customer

            severity = SEVERITY_FOR_LABEL.get(new_feasibility_label, "info")
            title = (
                f"{plan.crop.title()} plan analyzed"
                if is_first_analysis
                else f"{plan.crop.title()} plan status changed"
            )
            message = (
                f"Your '{plan.name}' plan is now classified as "
                f"'{new_feasibility_label.replace('_', ' ')}'."
            )

            notification = Notification(
                user_id=user.id,
                title=title,
                message=message,
                severity=severity,
                related_crop_plan_id=plan.id,
            )
            session.add(notification)
            session.flush()

            subject, text_body, html_body = build_feasibility_change_email(
                plan.crop, plan.location, old_label, new_feasibility_label
            )
            email_sent = send_email(user.email, subject, text_body, html_body)
            notification.email_sent = email_sent

            logger.info(
                f"Created notification {notification.id} for user_id={user.id} "
                f"crop_plan_id={crop_plan_id} (email_sent={email_sent})"
            )
    except Exception as exc:
        # A notification failing to generate must never fail the
        # analysis itself — the analysis result is already returned to
        # the client independently of this.
        logger.error(
            f"Failed to create notification for crop_plan_id={crop_plan_id}: {exc}",
            exc_info=True,
        )
