import pytest
import types

from talemate.history import shift_scene_timeline

# ---------------------------------------------------------------------------
# Fixtures
# ---------------------------------------------------------------------------


@pytest.fixture()
def dummy_scene():
    """Return a minimal Scene-like object whose attributes can be adjusted by
    individual tests. The fixture yields a *factory* so each test can create
    its own independent instance without repeating boilerplate."""

    def _factory(ts: str = "PT0S", archived=None, layered=None):
        scene = types.SimpleNamespace()
        scene.ts = ts
        scene.archived_history = archived or []
        scene.layered_history = layered or []
        return scene

    return _factory


# ---------------------------------------------------------------------------
# Tests
# ---------------------------------------------------------------------------


@pytest.mark.parametrize(
    "initial_ts, archived_tss, layered_tss, shift_iso, expected_scene_ts, expected_archived_ts, expected_layered_ts",
    [
        # Hours / minutes
        (
            "PT2H",  # initial
            ["PT1H", "PT90M"],
            ["PT30M"],
            "PT1H",  # +1 hour
            "PT3H",
            ["PT2H", "PT2H30M"],
            ["PT1H30M"],
        ),
        (
            "PT2H",
            ["PT1H", "PT90M"],
            ["PT30M"],
            "-PT30M",  # -30 minutes
            "PT1H30M",
            ["PT30M", "PT1H"],
            ["P0D"],  # zero becomes P0D
        ),
        # Months
        (
            "P2M",
            ["P1M", "P6M"],
            ["P3M"],
            "P1M",  # +1 month
            "P3M",
            ["P2M", "P7M"],
            ["P4M"],
        ),
        # Years
        (
            "P3Y",
            ["P1Y", "P2Y"],
            ["P6M"],
            "-P1Y",  # -1 year
            "P2Y",
            ["P0D", "P1Y"],
            ["P0D"],  # clamped to zero
        ),
        # Huge shift – 1000 years
        (
            "P0D",
            ["P1Y"],
            ["P2Y"],
            "P1000Y",
            "P1000Y",
            ["P1001Y"],
            ["P1002Y"],
        ),
    ],
    ids=["hour_plus", "hour_minus", "month_plus", "year_minus", "millennia_plus"],
)
def test_shift_scene_timeline_basic(
    dummy_scene,
    initial_ts,
    archived_tss,
    layered_tss,
    shift_iso,
    expected_scene_ts,
    expected_archived_ts,
    expected_layered_ts,
):
    """Parametrised test verifying various time-unit shifts."""

    archived = [{"ts": ts} for ts in archived_tss]
    layered = [[{"ts": ts} for ts in layered_tss]]

    scene = dummy_scene(initial_ts, archived, layered)

    shift_scene_timeline(scene, shift_iso)

    assert scene.ts == expected_scene_ts
    assert [e["ts"] for e in scene.archived_history] == expected_archived_ts
    assert [e["ts"] for e in scene.layered_history[0]] == expected_layered_ts


def test_shift_scene_timeline_noop(dummy_scene):
    """A shift of PT0S (and variants) should not mutate the scene."""

    scene = dummy_scene(
        "PT0S",
        archived=[{"ts": "PT1H", "ts_start": "PT30M", "ts_end": "PT90M"}],
        layered=[[{"ts": "PT15M"}]],
    )

    import copy

    pre_state = (
        scene.ts,
        copy.deepcopy(scene.archived_history),
        copy.deepcopy(scene.layered_history),
    )

    shift_scene_timeline(scene, "PT0S")

    assert scene.ts == pre_state[0]
    assert scene.archived_history == pre_state[1]
    assert scene.layered_history == pre_state[2]
