"""Shared CSV loading and validation logic.

Used by both the MCP server (``mixlift_mcp.server``) and the headless
Python API (``mixlift.api``).
"""

from __future__ import annotations

import pandas as pd


def detect_csv_format(df: pd.DataFrame) -> str:
    """Detect whether CSV is wide-format (demo-style) or platform export.

    Returns: 'wide', 'meta', 'google', 'tiktok', or 'generic'
    """
    cols_lower = {c.lower() for c in df.columns}

    # Wide format: has columns ending in _spend and a revenue/target column
    spend_cols = [c for c in df.columns if c.lower().endswith("_spend")]
    if len(spend_cols) >= 2 and any(
        c in cols_lower for c in ("revenue", "target", "sales")
    ):
        return "wide"

    # Delegate to the existing ingest service for platform detection
    from mixlift.ingest.service import detect_platform

    return detect_platform(df)


def validate_wide_format(df: pd.DataFrame, channel_columns: list[str]) -> list[str]:
    """Validate wide-format data with the same rigour as platform CSVs.

    Checks: NaN spend, negative spend, date range >= 90 days, >= 2 channels.
    Returns a list of error strings (empty if valid).
    """
    errors: list[str] = []

    # Channel count
    if len(channel_columns) < 2:
        errors.append(
            f"At least 2 spend channels required, but found {len(channel_columns)}."
        )

    # NaN spend
    for col in channel_columns:
        n_nan = int(df[col].isna().sum())
        if n_nan > 0:
            errors.append(f"Column '{col}' has {n_nan} missing (NaN) values.")

    # Negative spend
    for col in channel_columns:
        n_neg = int((df[col] < 0).sum())
        if n_neg > 0:
            errors.append(f"Column '{col}' has {n_neg} negative spend values.")

    # Date range
    try:
        dates = pd.to_datetime(df["date"])
        date_range_days = (dates.max() - dates.min()).days
        if date_range_days < 90:
            errors.append(
                f"Date range is {date_range_days} days; minimum 90 days required."
            )
    except Exception:
        errors.append("Could not parse 'date' column as dates.")

    return errors


def check_date_range_warnings(df: pd.DataFrame) -> list[str]:
    """Return warnings for date ranges between 90 and 179 days.

    Data with 90-179 days will still be analyzed, but with an accuracy caveat.
    Data with >=180 days produces no warning. Data with <90 days is blocked
    by validate_wide_format.
    """
    warnings: list[str] = []
    try:
        dates = pd.to_datetime(df["date"])
        date_range_days = (dates.max() - dates.min()).days
        if 90 <= date_range_days < 180:
            warnings.append(
                f"Your data covers {date_range_days} days. MixLift recommends "
                f"at least 180 days for reliable estimates. Results with "
                f"{date_range_days} days of data may have wider confidence "
                f"intervals and less stable channel attribution. Interpret "
                f"ROAS estimates with caution."
            )
    except Exception:
        pass
    return warnings


def load_wide_format(df: pd.DataFrame) -> tuple[pd.DataFrame, pd.Series, list[str]]:
    """Load wide-format CSV (like the bundled demo data) directly into model inputs.

    Wide format has: date, <channel>_spend, ..., revenue
    """
    from mixlift.modeling.preprocessing import add_control_columns

    df = df.copy()
    df["date"] = pd.to_datetime(df["date"])

    # Case-insensitive detection: match columns found during format detection
    channel_columns = [c for c in df.columns if c.lower().endswith("_spend")]

    # Target column
    target_col = None
    for candidate in ("revenue", "target", "sales"):
        if candidate in df.columns:
            target_col = candidate
            break

    if target_col is None:
        raise ValueError(
            "Wide-format CSV must have a 'revenue', 'target', or 'sales' column."
        )

    # Validate wide-format data (same checks as platform CSVs)
    validation_errors = validate_wide_format(df, channel_columns)
    if validation_errors:
        raise ValueError(
            "Data validation failed:\n" + "\n".join(f"  - {e}" for e in validation_errors)
        )

    y = df[target_col].astype(float)

    # Keep only date + spend columns, add controls
    X = df[["date"] + channel_columns].copy()
    X = add_control_columns(X)

    return X, y, channel_columns


def load_platform_csv(
    df: pd.DataFrame, platform: str
) -> tuple[pd.DataFrame, pd.Series, list[str]]:
    """Load a platform-specific or generic CSV via the ingest pipeline."""
    import logging

    from mixlift.ingest.service import parse_csv, validate_data
    from mixlift.modeling.preprocessing import prepare_model_data

    logger = logging.getLogger(__name__)

    try:
        canonical, detected = parse_csv(df, platform=platform)
    except ValueError:
        if platform != "generic":
            logger.info(
                "Platform parser '%s' failed, falling back to generic parser",
                platform,
            )
            canonical, detected = parse_csv(df, platform="generic")
        else:
            raise

    errors = validate_data(canonical)
    if errors:
        raise ValueError(
            "Data validation failed:\n" + "\n".join(f"  - {e}" for e in errors)
        )

    X, y, channel_columns = prepare_model_data(canonical)
    return X, y, channel_columns
