"""Tests for CSV parsers."""

import pandas as pd
import pytest

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.service import detect_platform, parse_csv, validate_data


class TestDetectPlatform:
    def test_detect_meta(self, sample_meta_csv):
        assert detect_platform(sample_meta_csv) == "meta"

    def test_detect_google(self, sample_google_csv):
        assert detect_platform(sample_google_csv) == "google"

    def test_detect_tiktok(self):
        df = pd.DataFrame({
            "Date": ["2024-01-01"],
            "Campaign Name": ["Test"],
            "Objective Type": ["REACH"],
            "Cost": [100],
            "Impression": [5000],
            "Click": [200],
        })
        assert detect_platform(df) == "tiktok"

    def test_detect_generic(self):
        df = pd.DataFrame({
            "date": ["2024-01-01"],
            "channel": ["email"],
            "spend": [50],
        })
        assert detect_platform(df) == "generic"

    def test_generic_csv_with_campaign_column_parses(self):
        """A generic CSV with a 'campaign' column should not crash.

        Regression test: 'campaign' used to match Google's identifiers, causing
        the Google parser to fail because it expects 'cost' not 'spend'.
        """
        dates = pd.date_range("2023-01-01", periods=210, freq="D")
        channels = ["Email", "Display"]
        rows = []
        for date in dates:
            for ch in channels:
                rows.append({
                    "date": date,
                    "campaign": f"{ch} Campaign",
                    "spend": 100.0,
                    "clicks": 50,
                    "impressions": 5000,
                    "conversions": 5,
                })
        df = pd.DataFrame(rows)
        # This should not raise — either Google parser handles "spend" alias
        # or the MCP server falls back to generic parser
        canonical, platform = parse_csv(df)
        assert "spend" in canonical.columns
        assert len(canonical) == 420


class TestMetaParser:
    def test_parse_meta(self, sample_meta_csv):
        result = parse_meta_csv(sample_meta_csv)
        assert list(result.columns) == ["date", "channel", "spend", "impressions", "clicks", "conversions"]
        assert len(result) == 200
        assert set(result["channel"]) == {"Prospecting", "Retargeting"}
        assert (result["spend"] >= 0).all()

    def test_meta_missing_columns(self):
        df = pd.DataFrame({"Date": ["2024-01-01"], "Weird Column": [1]})
        with pytest.raises(ValueError, match="missing required columns"):
            parse_meta_csv(df)


class TestGoogleParser:
    def test_parse_google(self, sample_google_csv):
        result = parse_google_csv(sample_google_csv)
        assert len(result) == 200
        assert set(result["channel"]) == {"Search", "Shopping"}

    def test_google_currency_stripping(self):
        df = pd.DataFrame({
            "Date": ["2024-01-01"],
            "Campaign": ["Test"],
            "Cost": ["$150.00"],
            "Impressions": ["8,000"],
            "Clicks": ["300"],
        })
        result = parse_google_csv(df)
        assert result["spend"].iloc[0] == 150.0
        assert result["impressions"].iloc[0] == 8000


class TestGenericParser:
    def test_parse_generic(self):
        df = pd.DataFrame({
            "date": pd.date_range("2024-01-01", periods=5),
            "channel": ["Email"] * 5,
            "spend": [50.0] * 5,
            "clicks": [100] * 5,
        })
        result = parse_generic_csv(df)
        assert len(result) == 5
        assert result["impressions"].sum() == 0  # no impressions column

    def test_generic_missing_spend(self):
        df = pd.DataFrame({"date": ["2024-01-01"], "other": [1]})
        with pytest.raises(ValueError, match="missing required columns"):
            parse_generic_csv(df)


class TestValidation:
    def test_valid_data(self, sample_canonical_df):
        errors = validate_data(sample_canonical_df)
        assert errors == []

    def test_negative_spend(self, sample_canonical_df):
        df = sample_canonical_df.copy()
        df.loc[0, "spend"] = -100
        errors = validate_data(df)
        assert any("negative spend" in e for e in errors)

    def test_too_few_channels(self):
        df = pd.DataFrame({
            "date": pd.date_range("2023-01-01", periods=200),
            "channel": ["Only One"] * 200,
            "spend": [100] * 200,
            "impressions": [1000] * 200,
            "clicks": [50] * 200,
            "conversions": [5] * 200,
        })
        errors = validate_data(df)
        assert any("at least 2 channels" in e for e in errors)

    def test_date_range_too_short(self):
        df = pd.DataFrame({
            "date": pd.date_range("2024-01-01", periods=30),
            "channel": ["A"] * 15 + ["B"] * 15,
            "spend": [100] * 30,
            "impressions": [1000] * 30,
            "clicks": [50] * 30,
            "conversions": [5] * 30,
        })
        errors = validate_data(df)
        assert any("90 days" in e for e in errors)

    def test_nan_spend(self):
        """NaN spend values should be caught by validation."""
        import numpy as np

        df = pd.DataFrame({
            "date": pd.date_range("2023-01-01", periods=210, freq="D"),
            "channel": ["A"] * 105 + ["B"] * 105,
            "spend": [100.0] * 209 + [np.nan],
            "impressions": [1000] * 210,
            "clicks": [50] * 210,
            "conversions": [5] * 210,
        })
        errors = validate_data(df)
        assert any("nan" in e.lower() or "missing" in e.lower() for e in errors)

    def test_empty_data(self):
        df = pd.DataFrame(columns=["date", "channel", "spend", "impressions", "clicks", "conversions"])
        errors = validate_data(df)
        assert any("empty" in e.lower() for e in errors)


class TestParseCSV:
    def test_auto_detect_and_parse_meta(self, sample_meta_csv):
        result, platform = parse_csv(sample_meta_csv)
        assert platform == "meta"
        assert len(result) == 200

    def test_force_platform(self, sample_meta_csv):
        # Force generic parser on meta data — should still work (date + spend exist)
        result, platform = parse_csv(sample_meta_csv, platform="generic")
        assert platform == "generic"
