"""Pipeline stage name constants and small shared helpers.

The constants here mirror `city_briefs_runs` columns verbatim. They are the
single source of truth for stage naming across the orchestrator, repositories,
and API layer.

Phase 12 deliberately excludes email stages — email is a future Phase 13.
"""

from __future__ import annotations

import logging
from datetime import date, datetime
from typing import Any

import pytz

from app.config import settings
from app.logging import redact_secrets

logger = logging.getLogger(__name__)

# Stage column names on city_briefs_runs, in execution order.
STAGE_HUB_LOOKUP = "hub_lookup"
STAGE_PREVIOUS_SHOW = "previous_show_lookup"
STAGE_HISTORICAL_SALES = "historical_sales_lookup"
STAGE_VOICE_PROFILE = "voice_profile_load"
STAGE_YOUTUBE_TRANSCRIPTS = "youtube_transcripts"
STAGE_PERPLEXITY_CITY = "perplexity_city"
STAGE_PERPLEXITY_NEWS = "perplexity_news"
STAGE_OPENAI_FACT_CHECK = "openai_fact_check"
STAGE_OPENAI_GENERATION = "openai_generation"
STAGE_CLAUDE_RANKING = "claude_ranking"
STAGE_HTML_GENERATION = "html_generation"
STAGE_VPS_DEPLOYMENT = "vps_deployment"
STAGE_URL_VERIFICATION = "url_verification"
STAGE_OG_VERIFICATION = "og_verification"

# Re-exported from models so callers can import from either place.
from app.models import STAGE_COLUMNS  # noqa: E402,F401


def today_in_app_tz() -> date:
    """Today's date in the configured app timezone (default LA)."""
    tz = pytz.timezone(settings.APP_TIMEZONE)
    return datetime.now(tz).date()


def to_date(value: Any) -> date | None:
    if value is None:
        return None
    if isinstance(value, date) and not isinstance(value, datetime):
        return value
    if isinstance(value, datetime):
        return value.date()
    if isinstance(value, str):
        try:
            return date.fromisoformat(value[:10])
        except ValueError:
            return None
    return None


def redact_for_storage(exc: BaseException) -> str:
    """Return a redacted, length-capped error representation for the DB row."""
    msg = f"{type(exc).__name__}: {exc}"
    return redact_secrets(msg)[:4000]


def as_int(value: Any) -> int | None:
    if value is None:
        return None
    try:
        return int(value)
    except (TypeError, ValueError):
        return None


def as_float(value: Any) -> float | None:
    if value is None:
        return None
    try:
        return float(value)
    except (TypeError, ValueError):
        return None


def brief_slug(city: str, show_date: date) -> str:
    """URL slug for a brief, e.g. `toronto-aug-11-2026`.

    Matches the format used in the planned public URL
    `https://just2done.com/brief/<slug>` and the HTML file written to the
    VPS webroot (`<slug>.html`).
    """
    city_part = (city or "").strip().lower().replace(" ", "-")
    date_part = show_date.strftime("%b-%d-%Y").lower()
    return f"{city_part}-{date_part}"