"""Recording and reading editor change history.

The Mix Editor and Ingredients Editor write a row here on every create/edit so
each mix and ingredient carries an auditable history (who changed what, when).
"""

from __future__ import annotations

from typing import Any

from sqlalchemy import select
from sqlalchemy.orm import Session

from app.api.deps import AuthSession
from app.models.change_event import EditorChangeEvent

# Entity types — these match the History buttons on the two editors.
ENTITY_MIX = "mix"
ENTITY_INGREDIENT = "ingredient"


def _stringify(value: Any) -> str | None:
    if value is None:
        return None
    if isinstance(value, bool):
        return "Yes" if value else "No"
    if isinstance(value, float):
        # Trim trailing zeros so 12.50 reads as 12.5 and 12.0 as 12.
        text = f"{value:.4f}".rstrip("0").rstrip(".")
        return text or "0"
    return str(value)


def diff_fields(before: dict[str, Any], after: dict[str, Any], labels: dict[str, str]) -> list[dict[str, Any]]:
    """Build a list of ``{field, label, before, after}`` deltas for changed fields.

    Only keys present in ``labels`` are considered, and only those whose value
    actually changed are emitted.
    """
    deltas: list[dict[str, Any]] = []
    for field, label in labels.items():
        if field not in after:
            continue
        old = before.get(field)
        new = after.get(field)
        if old == new:
            continue
        deltas.append({"field": field, "label": label, "before": _stringify(old), "after": _stringify(new)})
    return deltas


def record_change(
    db: Session,
    *,
    session: AuthSession,
    entity_type: str,
    entity_id: int,
    action: str,
    summary: str,
    changes: list[dict[str, Any]] | None = None,
) -> None:
    """Append a change event. Caller is responsible for committing the session."""
    db.add(
        EditorChangeEvent(
            tenant_id=session.tenant_id or "",
            entity_type=entity_type,
            entity_id=entity_id,
            action=action,
            actor_name=session.name or session.email or "Unknown",
            actor_email=session.email or "",
            actor_role=session.client_role or session.role,
            summary=summary,
            changes=changes or [],
        )
    )


def list_changes(
    db: Session,
    *,
    tenant_id: str,
    entity_type: str,
    entity_id: int,
    limit: int = 200,
) -> list[EditorChangeEvent]:
    return list(
        db.scalars(
            select(EditorChangeEvent)
            .where(
                EditorChangeEvent.tenant_id == tenant_id,
                EditorChangeEvent.entity_type == entity_type,
                EditorChangeEvent.entity_id == entity_id,
            )
            .order_by(EditorChangeEvent.created_at.desc(), EditorChangeEvent.id.desc())
            .limit(limit)
        ).all()
    )
