"""Tests for contextual memory system."""

import json
from unittest.mock import patch

import pytest

from mixlift_mcp.memory import (
    build_summary,
    compute_deltas,
    compute_fingerprint,
    load_previous,
    save_analysis,
)


class TestFingerprint:
    def test_stable_across_calls(self):
        fp1 = compute_fingerprint(["Meta", "Google"], "/data/ads.csv")
        fp2 = compute_fingerprint(["Meta", "Google"], "/data/ads.csv")
        assert fp1 == fp2

    def test_channel_order_independent(self):
        fp1 = compute_fingerprint(["Meta", "Google"], "/data/ads.csv")
        fp2 = compute_fingerprint(["Google", "Meta"], "/data/ads.csv")
        assert fp1 == fp2

    def test_different_files_different_fingerprint(self):
        fp1 = compute_fingerprint(["Meta"], "/data/ads.csv")
        fp2 = compute_fingerprint(["Meta"], "/data/other.csv")
        assert fp1 != fp2

    def test_different_channels_different_fingerprint(self):
        fp1 = compute_fingerprint(["Meta", "Google"], "/data/ads.csv")
        fp2 = compute_fingerprint(["Meta", "TikTok"], "/data/ads.csv")
        assert fp1 != fp2


class TestSaveAndLoad:
    def test_roundtrip(self, tmp_path, monkeypatch):
        import mixlift_mcp.memory as mem
        monkeypatch.setattr(mem, "MEMORY_DIR", tmp_path)

        fp = "test_fp_123"
        summary = {"timestamp": "2026-03-06T10:00:00", "roas": {"Meta": {"mean": 2.3}}}

        assert load_previous(fp) is None

        save_analysis(fp, "/data/ads.csv", summary)
        loaded = load_previous(fp)
        assert loaded is not None
        assert loaded["roas"]["Meta"]["mean"] == 2.3

    def test_fifo_eviction(self, tmp_path, monkeypatch):
        import mixlift_mcp.memory as mem
        monkeypatch.setattr(mem, "MEMORY_DIR", tmp_path)
        monkeypatch.setattr(mem, "MAX_ENTRIES_PER_DATASET", 3)

        fp = "test_evict"
        for i in range(5):
            save_analysis(fp, "test.csv", {"index": i})

        data = json.loads((tmp_path / f"{fp}.json").read_text())
        assert len(data["analyses"]) == 3
        assert data["analyses"][0]["index"] == 2  # oldest kept

    def test_load_nonexistent_returns_none(self, tmp_path, monkeypatch):
        import mixlift_mcp.memory as mem
        monkeypatch.setattr(mem, "MEMORY_DIR", tmp_path)
        assert load_previous("nonexistent") is None


class TestBuildSummary:
    def test_basic_summary(self):
        summary = build_summary(
            roas_estimates={"Meta": 2.3, "Google": 1.8},
            roas_ci={"Meta": (1.5, 3.1), "Google": (1.2, 2.4)},
            saturation_lights={"Meta": {"saturation_pct": 65}, "Google": {"saturation_pct": 40}},
            optimization={"current_allocation": {"Meta": 5000, "Google": 3000}, "expected_lift_pct": 8.5},
            channel_contributions={"Meta": 11500, "Google": 5400},
            model_fit={"r_squared": 0.87},
            data_weeks=30,
            analysis_badge=None,
        )
        assert summary["roas"]["Meta"]["mean"] == 2.3
        assert summary["roas"]["Google"]["ci_low"] == 1.2
        assert summary["saturation_pct"]["Meta"] == 65
        assert summary["contribution_share"]["Meta"] > 60  # Meta dominates
        assert summary["r_squared"] == 0.87
        assert summary["badge"] is None

    def test_exploratory_badge(self):
        summary = build_summary(
            roas_estimates={"Meta": 2.0},
            roas_ci={"Meta": (1.0, 3.0)},
            saturation_lights={},
            optimization={"current_allocation": {"Meta": 5000}, "expected_lift_pct": 5.0},
            channel_contributions={"Meta": 10000},
            model_fit={},
            data_weeks=18,
            analysis_badge={"type": "exploratory"},
        )
        assert summary["badge"] == "exploratory"


class TestComputeDeltas:
    def _make_summary(self, meta_mean, meta_ci_low, meta_ci_high, google_mean=1.8):
        return {
            "timestamp": "2026-03-06T10:00:00",
            "roas": {
                "Meta": {"mean": meta_mean, "ci_low": meta_ci_low, "ci_high": meta_ci_high},
                "Google": {"mean": google_mean, "ci_low": google_mean - 0.5, "ci_high": google_mean + 0.5},
            },
        }

    def test_clear_improvement(self):
        """Previous mean outside current 90% CI → high confidence."""
        previous = self._make_summary(2.0, 1.5, 2.5)
        current = self._make_summary(3.0, 2.5, 3.5)  # CI excludes prev mean of 2.0
        deltas = compute_deltas(current, previous)
        meta = deltas["changes"]["Meta"]
        assert meta["confidence"] == "high"
        assert meta["status"] == "changed"
        assert "clearly" in meta["narrative"]
        assert meta["change_pct"] == 50.0

    def test_moderate_change(self):
        """Previous mean inside current CI but >5% change → moderate confidence."""
        previous = self._make_summary(2.0, 1.5, 2.5)
        current = self._make_summary(2.5, 1.8, 3.2)  # CI includes prev mean of 2.0
        deltas = compute_deltas(current, previous)
        meta = deltas["changes"]["Meta"]
        assert meta["confidence"] == "moderate"
        assert "likely" in meta["narrative"]

    def test_noise_suppressed(self):
        """<5% relative change → stable regardless of CI."""
        previous = self._make_summary(2.0, 1.5, 2.5)
        current = self._make_summary(2.08, 1.5, 2.6)  # 4% change
        deltas = compute_deltas(current, previous)
        meta = deltas["changes"]["Meta"]
        assert meta["confidence"] == "low"
        assert meta["status"] == "stable"
        assert "unchanged" in meta["narrative"]

    def test_new_channel(self):
        previous = {"timestamp": "2026-03-01", "roas": {"Meta": {"mean": 2.0}}}
        current = {"roas": {"Meta": {"mean": 2.0, "ci_low": 1.5, "ci_high": 2.5}, "TikTok": {"mean": 3.0, "ci_low": 2.0, "ci_high": 4.0}}}
        deltas = compute_deltas(current, previous)
        assert deltas["changes"]["TikTok"]["status"] == "new"

    def test_dropped_channel(self):
        previous = {"timestamp": "2026-03-01", "roas": {"Meta": {"mean": 2.0}, "TikTok": {"mean": 3.0}}}
        current = {"roas": {"Meta": {"mean": 2.0, "ci_low": 1.5, "ci_high": 2.5}}}
        deltas = compute_deltas(current, previous)
        assert deltas["changes"]["TikTok"]["status"] == "dropped"
