"""Tests for each MCP tool using the mock model fixture.

Tests cover:
- Correct output schema (expected keys present)
- Numerical correctness against known fixture values
- Channel filtering
- Edge cases
"""

import numpy as np
import pytest

from tests.conftest import (
    ALPHA_VALUES,
    CHANNELS,
    EC_VALUES,
    N_CHAINS,
    N_DRAWS,
    N_TIME,
    ROI_VALUES,
    SPEND_PER_CHANNEL,
    TIME,
)

# ---------------------------------------------------------------------------
# get_model_summary
# ---------------------------------------------------------------------------


class TestGetModelSummary:
    def test_returns_expected_keys(self, mock_model):
        from mcp_server_meridian.server import get_model_summary

        result = get_model_summary()
        assert "model_name" in result
        assert "media_channels" in result
        assert "n_chains" in result
        assert "n_draws" in result
        assert "time_range" in result
        assert "convergence" in result

    def test_channel_count(self, mock_model):
        from mcp_server_meridian.server import get_model_summary

        result = get_model_summary()
        assert result["n_channels"] == len(CHANNELS)
        assert result["media_channels"] == CHANNELS

    def test_chain_draw_counts(self, mock_model):
        from mcp_server_meridian.server import get_model_summary

        result = get_model_summary()
        assert result["n_chains"] == N_CHAINS
        assert result["n_draws"] == N_DRAWS

    def test_convergence_no_divergences(self, mock_model):
        from mcp_server_meridian.server import get_model_summary

        result = get_model_summary()
        conv = result["convergence"]
        assert conv["has_divergences"] is False
        assert conv["n_divergences"] == 0
        assert conv["mean_accept_ratio"] == pytest.approx(0.85)

    def test_model_spec_fields(self, mock_model):
        from mcp_server_meridian.server import get_model_summary

        result = get_model_summary()
        assert result["max_lag"] == 4
        assert result["hill_before_adstock"] is False


# ---------------------------------------------------------------------------
# get_channel_roi
# ---------------------------------------------------------------------------


class TestGetChannelRoi:
    def test_returns_all_channels(self, mock_model):
        from mcp_server_meridian.server import get_channel_roi

        result = get_channel_roi()
        names = [c["name"] for c in result["channels"]]
        assert names == CHANNELS

    def test_median_close_to_known_value(self, mock_model):
        from mcp_server_meridian.server import get_channel_roi

        result = get_channel_roi()
        for ch_result in result["channels"]:
            expected = ROI_VALUES[ch_result["name"]]
            # We added noise of 0.1, so median should be close
            assert ch_result["roi_median"] == pytest.approx(expected, abs=0.15)

    def test_ci_lower_less_than_upper(self, mock_model):
        from mcp_server_meridian.server import get_channel_roi

        result = get_channel_roi()
        for ch in result["channels"]:
            assert ch["roi_ci_lower"] < ch["roi_ci_upper"]
            assert ch["roi_ci_lower"] <= ch["roi_median"] <= ch["roi_ci_upper"]

    def test_prob_positive_for_positive_roi(self, mock_model):
        from mcp_server_meridian.server import get_channel_roi

        result = get_channel_roi()
        for ch in result["channels"]:
            # All ROI values are positive (0.5+), with noise of 0.1 should stay positive
            assert ch["prob_positive"] > 0.95

    def test_filter_single_channel(self, mock_model):
        from mcp_server_meridian.server import get_channel_roi

        result = get_channel_roi(channels=["channel_b"])
        assert len(result["channels"]) == 1
        assert result["channels"][0]["name"] == "channel_b"

    def test_invalid_channel_raises(self, mock_model):
        from mcp_server_meridian.server import get_channel_roi

        with pytest.raises(ValueError, match="Unknown channels"):
            get_channel_roi(channels=["nonexistent"])

    def test_wider_ci_is_wider(self, mock_model):
        from mcp_server_meridian.server import get_channel_roi

        narrow = get_channel_roi(credible_interval=0.5)
        wide = get_channel_roi(credible_interval=0.99)
        for n, w in zip(narrow["channels"], wide["channels"]):
            n_width = n["roi_ci_upper"] - n["roi_ci_lower"]
            w_width = w["roi_ci_upper"] - w["roi_ci_lower"]
            assert w_width >= n_width

    def test_n_samples(self, mock_model):
        from mcp_server_meridian.server import get_channel_roi

        result = get_channel_roi()
        assert result["n_samples"] == N_CHAINS * N_DRAWS


# ---------------------------------------------------------------------------
# get_adstock_parameters
# ---------------------------------------------------------------------------


class TestGetAdstockParameters:
    def test_returns_all_channels(self, mock_model):
        from mcp_server_meridian.server import get_adstock_parameters

        result = get_adstock_parameters()
        assert len(result["channels"]) == len(CHANNELS)
        assert result["adstock_type"] == "geometric"

    def test_decay_rate_matches_fixture(self, mock_model):
        from mcp_server_meridian.server import get_adstock_parameters

        result = get_adstock_parameters()
        for ch in result["channels"]:
            expected = ALPHA_VALUES[ch["name"]]
            assert ch["decay_rate_median"] == pytest.approx(expected)

    def test_half_life_known_value(self, mock_model):
        from mcp_server_meridian.server import get_adstock_parameters

        result = get_adstock_parameters()
        # channel_a: alpha=0.5 → half_life = log(0.5)/log(0.5) = 1.0
        ch_a = next(c for c in result["channels"] if c["name"] == "channel_a")
        assert ch_a["half_life_weeks"] == pytest.approx(1.0)

    def test_higher_decay_longer_duration(self, mock_model):
        from mcp_server_meridian.server import get_adstock_parameters

        result = get_adstock_parameters()
        by_name = {c["name"]: c for c in result["channels"]}
        # channel_c (α=0.75) should have longer duration than channel_b (α=0.25)
        assert (
            by_name["channel_c"]["effect_duration_weeks"]
            > by_name["channel_b"]["effect_duration_weeks"]
        )

    def test_duration_gt_halflife(self, mock_model):
        from mcp_server_meridian.server import get_adstock_parameters

        result = get_adstock_parameters()
        for ch in result["channels"]:
            assert ch["effect_duration_weeks"] > ch["half_life_weeks"]

    def test_filter_channel(self, mock_model):
        from mcp_server_meridian.server import get_adstock_parameters

        result = get_adstock_parameters(channels=["channel_c"])
        assert len(result["channels"]) == 1


# ---------------------------------------------------------------------------
# get_saturation_curves
# ---------------------------------------------------------------------------


class TestGetSaturationCurves:
    def test_returns_all_channels(self, mock_model):
        from mcp_server_meridian.server import get_saturation_curves

        result = get_saturation_curves()
        assert len(result["channels"]) == len(CHANNELS)
        assert "saturation_labels" in result

    def test_ec_matches_fixture(self, mock_model):
        from mcp_server_meridian.server import get_saturation_curves

        result = get_saturation_curves()
        for ch in result["channels"]:
            expected = EC_VALUES[ch["name"]]
            assert ch["ec_median"] == pytest.approx(expected)

    def test_saturation_pct_bounded(self, mock_model):
        from mcp_server_meridian.server import get_saturation_curves

        result = get_saturation_curves()
        for ch in result["channels"]:
            if ch.get("current_saturation_pct") is not None:
                assert 0 <= ch["current_saturation_pct"] <= 100

    def test_saturation_label_valid(self, mock_model):
        from mcp_server_meridian.server import get_saturation_curves

        result = get_saturation_curves()
        valid_labels = {"low", "moderate", "high", "very_high", "unknown"}
        for ch in result["channels"]:
            assert ch["saturation_label"] in valid_labels

    def test_has_interpretation(self, mock_model):
        from mcp_server_meridian.server import get_saturation_curves

        result = get_saturation_curves()
        for ch in result["channels"]:
            if ch.get("current_saturation_pct") is not None:
                assert ch["interpretation"] is not None
                assert "saturation" in ch["interpretation"]


# ---------------------------------------------------------------------------
# get_marginal_roi
# ---------------------------------------------------------------------------


class TestGetMarginalRoi:
    def test_returns_all_channels(self, mock_model):
        from mcp_server_meridian.server import get_marginal_roi

        result = get_marginal_roi()
        assert len(result["channels"]) == len(CHANNELS)
        assert "interpretation" in result

    def test_mroi_is_finite(self, mock_model):
        from mcp_server_meridian.server import get_marginal_roi

        result = get_marginal_roi()
        for ch in result["channels"]:
            assert np.isfinite(ch["mroi_median"])
            assert np.isfinite(ch["mroi_mean"])

    def test_headroom_signal_valid(self, mock_model):
        from mcp_server_meridian.server import get_marginal_roi

        result = get_marginal_roi()
        valid = {"high", "moderate", "low"}
        for ch in result["channels"]:
            assert ch["headroom_signal"] in valid

    def test_ci_ordering(self, mock_model):
        from mcp_server_meridian.server import get_marginal_roi

        result = get_marginal_roi()
        for ch in result["channels"]:
            assert ch["mroi_ci_lower"] <= ch["mroi_ci_upper"]

    def test_mroi_non_negative(self, mock_model):
        """With positive spend and positive betas, mROI should be non-negative."""
        from mcp_server_meridian.server import get_marginal_roi

        result = get_marginal_roi()
        for ch in result["channels"]:
            assert ch["mroi_median"] >= 0


# ---------------------------------------------------------------------------
# get_contribution_breakdown
# ---------------------------------------------------------------------------


class TestGetContributionBreakdown:
    def test_returns_all_channels(self, mock_model):
        from mcp_server_meridian.server import get_contribution_breakdown

        result = get_contribution_breakdown()
        assert len(result["channels"]) == len(CHANNELS)
        assert "total_modeled_contribution_median" in result

    def test_contributions_positive(self, mock_model):
        from mcp_server_meridian.server import get_contribution_breakdown

        result = get_contribution_breakdown()
        for ch in result["channels"]:
            assert ch["contribution_median"] > 0

    def test_percentages_sum_near_100(self, mock_model):
        from mcp_server_meridian.server import get_contribution_breakdown

        result = get_contribution_breakdown(as_percentage=True)
        total_pct = sum(ch["contribution_pct"] for ch in result["channels"])
        assert total_pct == pytest.approx(100.0, abs=5.0)  # median percentages may not sum exactly

    def test_no_percentage_when_disabled(self, mock_model):
        from mcp_server_meridian.server import get_contribution_breakdown

        result = get_contribution_breakdown(as_percentage=False)
        for ch in result["channels"]:
            assert "contribution_pct" not in ch

    def test_ci_ordering(self, mock_model):
        from mcp_server_meridian.server import get_contribution_breakdown

        result = get_contribution_breakdown()
        for ch in result["channels"]:
            assert ch["contribution_ci_lower"] <= ch["contribution_ci_upper"]


# ---------------------------------------------------------------------------
# simulate_budget_reallocation
# ---------------------------------------------------------------------------


class TestSimulateBudgetReallocation:
    def test_unchanged_budget_zero_change(self, mock_model):
        from mcp_server_meridian.server import simulate_budget_reallocation

        current = {ch: s for ch, s in zip(CHANNELS, SPEND_PER_CHANNEL)}
        result = simulate_budget_reallocation(proposed_budgets=current)
        assert result["spend_change_pct"] == pytest.approx(0.0, abs=0.1)
        assert result["projected_revenue"]["change_median"] == pytest.approx(0.0, abs=1.0)

    def test_invalid_channel_returns_error(self, mock_model):
        from mcp_server_meridian.server import simulate_budget_reallocation

        result = simulate_budget_reallocation(proposed_budgets={"fake_channel": 1000})
        assert "error" in result

    def test_output_schema(self, mock_model):
        from mcp_server_meridian.server import simulate_budget_reallocation

        result = simulate_budget_reallocation(
            proposed_budgets={"channel_a": 1500},
            n_weeks=4,
        )
        assert "current_total_weekly_spend" in result
        assert "proposed_total_weekly_spend" in result
        assert "projected_revenue" in result
        assert "channel_impacts" in result
        assert "optimization_notes" in result
        rev = result["projected_revenue"]
        assert "current_median" in rev
        assert "proposed_median" in rev
        assert "proposed_ci_lower" in rev
        assert "proposed_ci_upper" in rev

    def test_increased_spend_increases_contribution(self, mock_model):
        from mcp_server_meridian.server import simulate_budget_reallocation

        # Double channel_a spend
        result = simulate_budget_reallocation(proposed_budgets={"channel_a": 2000})
        rev = result["projected_revenue"]
        assert rev["proposed_median"] >= rev["current_median"]

    def test_channels_not_in_proposal_keep_current(self, mock_model):
        from mcp_server_meridian.server import simulate_budget_reallocation

        # Only change channel_a, others should keep current spend
        result = simulate_budget_reallocation(proposed_budgets={"channel_a": 1500})
        assert result["proposed_total_weekly_spend"] == pytest.approx(
            1500 + SPEND_PER_CHANNEL[1] + SPEND_PER_CHANNEL[2], abs=1.0
        )

    def test_n_weeks_scales_revenue(self, mock_model):
        from mcp_server_meridian.server import simulate_budget_reallocation

        current = {ch: s for ch, s in zip(CHANNELS, SPEND_PER_CHANNEL)}
        r4 = simulate_budget_reallocation(proposed_budgets=current, n_weeks=4)
        r8 = simulate_budget_reallocation(proposed_budgets=current, n_weeks=8)
        # 8 weeks should be ~2x 4 weeks
        ratio = (
            r8["projected_revenue"]["current_median"]
            / r4["projected_revenue"]["current_median"]
        )
        assert ratio == pytest.approx(2.0, rel=0.01)

    def test_revenue_values_finite(self, mock_model):
        from mcp_server_meridian.server import simulate_budget_reallocation

        result = simulate_budget_reallocation(proposed_budgets={"channel_a": 500})
        rev = result["projected_revenue"]
        for key in ["current_median", "proposed_median", "proposed_ci_lower", "proposed_ci_upper"]:
            assert np.isfinite(rev[key])

    def test_ci_ordering(self, mock_model):
        from mcp_server_meridian.server import simulate_budget_reallocation

        result = simulate_budget_reallocation(proposed_budgets={"channel_a": 500})
        rev = result["projected_revenue"]
        assert rev["proposed_ci_lower"] <= rev["proposed_median"] <= rev["proposed_ci_upper"]


# ---------------------------------------------------------------------------
# get_weekly_contributions
# ---------------------------------------------------------------------------


class TestGetWeeklyContributions:
    def test_returns_all_channels_all_weeks(self, mock_model):
        from mcp_server_meridian.server import get_weekly_contributions

        result = get_weekly_contributions()
        assert len(result["channels"]) == len(CHANNELS)
        assert result["n_weeks"] == N_TIME
        for ch in result["channels"]:
            assert len(ch["weeks"]) == N_TIME
            assert len(ch["contribution_median"]) == N_TIME

    def test_last_n_weeks(self, mock_model):
        from mcp_server_meridian.server import get_weekly_contributions

        result = get_weekly_contributions(last_n_weeks=8)
        assert result["n_weeks"] == 8
        for ch in result["channels"]:
            assert len(ch["weeks"]) == 8
            assert len(ch["contribution_median"]) == 8
        # Should be the last 8 weeks
        assert result["time_range"]["end"] == TIME[-1]

    def test_filter_channel(self, mock_model):
        from mcp_server_meridian.server import get_weekly_contributions

        result = get_weekly_contributions(channels=["channel_b"])
        assert len(result["channels"]) == 1
        assert result["channels"][0]["name"] == "channel_b"

    def test_start_end_week(self, mock_model):
        from mcp_server_meridian.server import get_weekly_contributions

        start = TIME[10]
        end = TIME[19]
        result = get_weekly_contributions(start_week=start, end_week=end)
        assert result["n_weeks"] == 10
        assert result["time_range"]["start"] == start
        assert result["time_range"]["end"] == end

    def test_contributions_positive(self, mock_model):
        from mcp_server_meridian.server import get_weekly_contributions

        result = get_weekly_contributions()
        for ch in result["channels"]:
            for val in ch["contribution_median"]:
                assert val >= 0

    def test_ci_ordering(self, mock_model):
        from mcp_server_meridian.server import get_weekly_contributions

        result = get_weekly_contributions(last_n_weeks=4)
        for ch in result["channels"]:
            for lo, med, hi in zip(
                ch["contribution_ci_lower"],
                ch["contribution_median"],
                ch["contribution_ci_upper"],
            ):
                assert lo <= med <= hi

    def test_total_equals_sum(self, mock_model):
        from mcp_server_meridian.server import get_weekly_contributions

        result = get_weekly_contributions(last_n_weeks=4)
        for ch in result["channels"]:
            assert ch["total_median"] == pytest.approx(sum(ch["contribution_median"]), abs=0.1)

    def test_has_time_range(self, mock_model):
        from mcp_server_meridian.server import get_weekly_contributions

        result = get_weekly_contributions()
        assert result["time_range"]["start"] == TIME[0]
        assert result["time_range"]["end"] == TIME[-1]

    def test_last_n_weeks_overrides_start_end(self, mock_model):
        from mcp_server_meridian.server import get_weekly_contributions

        result = get_weekly_contributions(last_n_weeks=4, start_week=TIME[0], end_week=TIME[10])
        # last_n_weeks should take priority
        assert result["n_weeks"] == 4


# ---------------------------------------------------------------------------
# get_channel_priors
# ---------------------------------------------------------------------------


class TestGetChannelPriors:
    def test_returns_global_priors(self, mock_model):
        from mcp_server_meridian.server import get_channel_priors

        result = get_channel_priors()
        assert "error" not in result
        assert result["media_prior_type"] == "roi"
        assert result["media_effects_dist"] == "log_normal"
        assert "global_priors" in result

    def test_roi_prior_params(self, mock_model):
        from mcp_server_meridian.server import get_channel_priors

        result = get_channel_priors()
        roi = result["global_priors"]["roi_m"]
        assert roi["distribution"] == "LogNormal"
        assert roi["loc"] == pytest.approx(0.2, abs=0.01)
        assert roi["scale"] == pytest.approx(0.9, abs=0.01)

    def test_alpha_prior_params(self, mock_model):
        from mcp_server_meridian.server import get_channel_priors

        result = get_channel_priors()
        alpha = result["global_priors"]["alpha_m"]
        assert alpha["distribution"] == "Uniform"
        assert alpha["low"] == pytest.approx(0.0)
        assert alpha["high"] == pytest.approx(1.0)

    def test_ec_prior_params(self, mock_model):
        from mcp_server_meridian.server import get_channel_priors

        result = get_channel_priors()
        ec = result["global_priors"]["ec_m"]
        assert ec["distribution"] == "TruncatedNormal"
        assert ec["low"] == pytest.approx(0.1)
        assert ec["high"] == pytest.approx(10.0)

    def test_slope_prior_deterministic(self, mock_model):
        from mcp_server_meridian.server import get_channel_priors

        result = get_channel_priors()
        slope = result["global_priors"]["slope_m"]
        assert slope["distribution"] == "Deterministic"
        assert slope["loc"] == pytest.approx(1.0)

    def test_applies_to_all_channels(self, mock_model):
        from mcp_server_meridian.server import get_channel_priors

        result = get_channel_priors()
        assert result["applies_to_channels"] == CHANNELS

    def test_filter_channels(self, mock_model):
        from mcp_server_meridian.server import get_channel_priors

        result = get_channel_priors(channels=["channel_a"])
        assert result["applies_to_channels"] == ["channel_a"]

    def test_has_descriptions(self, mock_model):
        from mcp_server_meridian.server import get_channel_priors

        result = get_channel_priors()
        for prior_info in result["global_priors"].values():
            assert "description" in prior_info

    def test_priors_not_per_channel(self, mock_model):
        from mcp_server_meridian.server import get_channel_priors

        result = get_channel_priors()
        assert result["priors_are_per_channel"] is False


# ---------------------------------------------------------------------------
# get_prior_posterior_comparison
# ---------------------------------------------------------------------------


class TestGetPriorPosteriorComparison:
    def test_returns_all_channels(self, mock_model):
        from mcp_server_meridian.server import get_prior_posterior_comparison

        result = get_prior_posterior_comparison()
        assert "error" not in result
        assert len(result["channels"]) == len(CHANNELS)
        names = [c["name"] for c in result["channels"]]
        assert names == CHANNELS

    def test_has_parameters(self, mock_model):
        from mcp_server_meridian.server import get_prior_posterior_comparison

        result = get_prior_posterior_comparison()
        for ch in result["channels"]:
            assert len(ch["parameters"]) > 0
            for p in ch["parameters"]:
                assert "parameter" in p
                assert "label" in p
                assert "prior" in p
                assert "posterior" in p

    def test_overlap_bounded(self, mock_model):
        from mcp_server_meridian.server import get_prior_posterior_comparison

        result = get_prior_posterior_comparison()
        for ch in result["channels"]:
            for p in ch["parameters"]:
                assert 0 <= p["overlap_coefficient"] <= 1

    def test_data_influence_bounded(self, mock_model):
        from mcp_server_meridian.server import get_prior_posterior_comparison

        result = get_prior_posterior_comparison()
        for ch in result["channels"]:
            for p in ch["parameters"]:
                assert 0 <= p["data_influence"] <= 1

    def test_influence_label_valid(self, mock_model):
        from mcp_server_meridian.server import get_prior_posterior_comparison

        valid = {
            "prior_dominated", "weakly_informed",
            "moderately_informed", "data_driven",
        }
        result = get_prior_posterior_comparison()
        for ch in result["channels"]:
            for p in ch["parameters"]:
                assert p["influence_label"] in valid

    def test_prior_posterior_stats(self, mock_model):
        from mcp_server_meridian.server import get_prior_posterior_comparison

        result = get_prior_posterior_comparison()
        ch = result["channels"][0]
        for p in ch["parameters"]:
            prior = p["prior"]
            post = p["posterior"]
            assert "median" in prior
            assert "ci_lower" in prior
            assert "ci_upper" in prior
            assert "median" in post
            assert prior["ci_lower"] <= prior["ci_upper"]
            assert post["ci_lower"] <= post["ci_upper"]

    def test_filter_single_channel(self, mock_model):
        from mcp_server_meridian.server import get_prior_posterior_comparison

        result = get_prior_posterior_comparison(channels=["channel_b"])
        assert len(result["channels"]) == 1
        assert result["channels"][0]["name"] == "channel_b"

    def test_has_interpretation(self, mock_model):
        from mcp_server_meridian.server import get_prior_posterior_comparison

        result = get_prior_posterior_comparison()
        assert "interpretation" in result
        assert "overlap_coefficient" in result["interpretation"]
        assert "data_influence" in result["interpretation"]
        assert "influence_labels" in result["interpretation"]

    def test_concentration_positive_when_posterior_narrower(self, mock_model):
        """With deterministic fixture values, posterior should be much
        narrower than the prior for most parameters."""
        from mcp_server_meridian.server import get_prior_posterior_comparison

        result = get_prior_posterior_comparison()
        # alpha_m: prior is Uniform(0,1), posterior is tight around 0.5
        ch_a = result["channels"][0]
        alpha_param = next(
            p for p in ch_a["parameters"] if p["parameter"] == "alpha_m"
        )
        assert alpha_param["concentration"] > 0


# ---------------------------------------------------------------------------
# get_model_settings
# ---------------------------------------------------------------------------


class TestGetModelSettings:
    def test_returns_expected_sections(self, mock_model):
        from mcp_server_meridian.server import get_model_settings

        result = get_model_settings()
        assert "mcmc" in result
        assert "holdout" in result
        assert "prior_parameterization" in result
        assert "model_structure" in result

    def test_mcmc_values(self, mock_model):
        from mcp_server_meridian.server import get_model_settings

        result = get_model_settings()
        mcmc = result["mcmc"]
        assert mcmc["n_chains"] == N_CHAINS
        assert mcmc["n_draws"] == N_DRAWS
        assert mcmc["n_warmup"] == "not_stored"
        assert mcmc["n_divergences"] == 0
        assert abs(mcmc["realized_accept_ratio"] - 0.85) < 0.01

    def test_holdout_not_stored(self, mock_model):
        from mcp_server_meridian.server import get_model_settings

        result = get_model_settings()
        holdout = result["holdout"]
        assert holdout["strategy"] == "not_stored"
        assert holdout["n_holdout_points"] == "not_stored"
        assert holdout["random_seed"] == "not_stored"

    def test_prior_parameterization(self, mock_model):
        from mcp_server_meridian.server import get_model_settings

        result = get_model_settings()
        priors = result["prior_parameterization"]
        assert priors["media_prior_type"] == "roi"
        assert priors["media_effects_dist"] == "log_normal"
        assert "roi_prior_example" in priors
        example = priors["roi_prior_example"]
        assert example["distribution"] == "LogNormal"
        assert abs(example["parameters"]["loc"] - 0.2) < 0.01
        assert abs(example["parameters"]["scale"] - 0.9) < 0.01
        assert "No additional transformations" in example["note"]

    def test_model_structure(self, mock_model):
        from mcp_server_meridian.server import get_model_settings

        result = get_model_settings()
        structure = result["model_structure"]
        assert structure["max_lag"] == 4
        assert structure["hill_before_adstock"] is False

    def test_guidance_for_missing(self, mock_model):
        from mcp_server_meridian.server import get_model_settings

        result = get_model_settings()
        assert result["guidance_for_missing"] is not None
        assert "posterior.attrs" in result["guidance_for_missing"]

    def test_stored_attrs_are_returned(self, mock_model):
        """When training attrs are present, they should be returned."""
        from mcp_server_meridian.server import _get_posterior, get_model_settings

        posterior = _get_posterior()
        posterior.attrs["n_warmup"] = 500
        posterior.attrs["holdout_strategy"] = "last_8_weeks"
        posterior.attrs["n_holdout_points"] = 8
        posterior.attrs["holdout_seed"] = 42
        posterior.attrs["target_accept_rate"] = 0.8

        result = get_model_settings()
        assert result["mcmc"]["n_warmup"] == 500
        assert result["mcmc"]["target_accept_rate"] == 0.8
        assert result["holdout"]["strategy"] == "last_8_weeks"
        assert result["holdout"]["n_holdout_points"] == 8
        assert result["holdout"]["random_seed"] == 42
        assert result["guidance_for_missing"] is None
