"""Ingest service: detect platform, parse, validate CSV uploads."""

from pathlib import Path

import pandas as pd

from mixlift.ingest.parsers.generic import parse_generic_csv
from mixlift.ingest.parsers.google import parse_google_csv
from mixlift.ingest.parsers.meta import parse_meta_csv
from mixlift.ingest.parsers.tiktok import parse_tiktok_csv
from mixlift.ingest.templates import PLATFORM_SIGNATURES


def detect_platform(df: pd.DataFrame) -> str:
    """Auto-detect the ad platform based on column names.

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

    scores = {}
    for platform, config in PLATFORM_SIGNATURES.items():
        matches = sum(1 for ident in config["identifiers"] if ident.lower() in lower_cols)
        if matches > 0:
            scores[platform] = matches

    if scores:
        return max(scores, key=scores.get)
    return "generic"


def parse_csv(df: pd.DataFrame, platform: str | None = None) -> tuple[pd.DataFrame, str]:
    """Parse a CSV DataFrame into canonical format.

    Args:
        df: Raw CSV as DataFrame
        platform: Force a specific platform parser. Auto-detects if None.

    Returns:
        Tuple of (canonical DataFrame, detected platform name)
    """
    if platform is None:
        platform = detect_platform(df)

    parsers = {
        "meta": parse_meta_csv,
        "google": parse_google_csv,
        "tiktok": parse_tiktok_csv,
        "generic": parse_generic_csv,
    }

    parser = parsers.get(platform, parse_generic_csv)
    canonical = parser(df)
    return canonical, platform


def validate_data(df: pd.DataFrame) -> list[str]:
    """Validate canonical-format data. Returns list of error messages (empty = valid)."""
    errors = []

    if df.empty:
        errors.append("Dataset is empty.")
        return errors

    # Check for NaN spend
    if df["spend"].isna().any():
        n_nan = int(df["spend"].isna().sum())
        errors.append(f"Found {n_nan} rows with missing/NaN spend values.")

    # Check for negative spend
    if (df["spend"] < 0).any():
        n_negative = (df["spend"] < 0).sum()
        errors.append(f"Found {n_negative} rows with negative spend values.")

    # Check minimum channels
    n_channels = df["channel"].nunique()
    if n_channels < 2:
        errors.append(f"Need at least 2 channels, found {n_channels}.")

    # Check minimum date range (90 days, aligned with wide-format path)
    date_range = (df["date"].max() - df["date"].min()).days
    if date_range < 90:
        errors.append(
            f"Date range is {date_range} days. Need at least 90 days "
            f"for minimum viable modeling."
        )

    # Check for unparseable dates
    if df["date"].isna().any():
        n_bad = df["date"].isna().sum()
        errors.append(f"Found {n_bad} rows with unparseable dates.")

    return errors


def load_and_parse(filepath: str | Path, platform: str | None = None) -> tuple[pd.DataFrame, str]:
    """Load a CSV file, auto-detect platform, parse, and validate.

    Returns:
        Tuple of (canonical DataFrame, platform name)

    Raises:
        ValueError: If validation fails
    """
    df = pd.read_csv(filepath)
    canonical, platform = parse_csv(df, platform)

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

    return canonical, platform


def save_as_parquet(df: pd.DataFrame, output_path: str | Path) -> Path:
    """Save canonical DataFrame as Parquet file."""
    output_path = Path(output_path)
    output_path.parent.mkdir(parents=True, exist_ok=True)
    df.to_parquet(output_path, index=False)
    return output_path
