import time
from datetime import datetime

import pytest

from tklr.tklr_env import TklrEnvironment
from tklr.model import DatabaseManager
from tklr.item import Item


@pytest.fixture
def isolated_env(tmp_path, monkeypatch):
    """Create a throwaway TKLR_HOME so tests never touch local data."""
    home = tmp_path / "tklr-home"
    monkeypatch.delenv("TKLR_HOME", raising=False)
    monkeypatch.delenv("XDG_CONFIG_HOME", raising=False)
    monkeypatch.setenv("TKLR_HOME", str(home))

    env = TklrEnvironment()
    env.ensure(init_config=True, init_db_fn=None)
    return env


@pytest.mark.parametrize("tz_name", ["Etc/GMT+5"])
def test_rdate_rows_use_local_time(isolated_env, monkeypatch, tz_name):
    """
    Regression test: RDATE-only reminders must emit local-naive timestamps
    into DateTimes even though the rruleset stores UTC ('...Z').
    """

    monkeypatch.setenv("TZ", tz_name)
    if hasattr(time, "tzset"):
        time.tzset()

    dbm = DatabaseManager(
        str(isolated_env.db_path),
        isolated_env,
        reset=True,
        auto_populate=False,
    )

    single = Item(
        env=isolated_env,
        raw="* single @s 2026-01-08 11:00 @e 1h",
        final=True,
    )
    recurring = Item(
        env=isolated_env,
        raw="* repeating @s 2026-01-08 11:00 @e 1h @r w",
        final=True,
    )

    single_id = dbm.add_item(single)
    repeat_id = dbm.add_item(recurring)

    dbm.generate_datetimes_for_record(single_id, clear_existing=True)
    dbm.generate_datetimes_for_record(repeat_id, clear_existing=True)
    dbm.conn.commit()

    (rruleset_single,) = dbm.cursor.execute(
        "SELECT rruleset FROM Records WHERE id=?", (single_id,)
    ).fetchone()
    assert "RDATE:20260108T1600Z" in rruleset_single

    rows = dbm.cursor.execute(
        "SELECT start_datetime FROM DateTimes WHERE record_id=? ORDER BY start_datetime",
        (single_id,),
    ).fetchall()
    assert rows == [("20260108T1100",)]

    repeat_rows = dbm.cursor.execute(
        "SELECT start_datetime FROM DateTimes WHERE record_id=? ORDER BY start_datetime LIMIT 2",
        (repeat_id,),
    ).fetchall()
    assert repeat_rows[0][0].endswith("1100")
    assert repeat_rows[1][0].endswith("1100")

    dbm.conn.close()


@pytest.mark.parametrize("tz_name", ["US/Eastern"])
def test_aware_weekly_recurrence_keeps_local_wall_time_across_dst(
    isolated_env, monkeypatch, tz_name
):
    """
    Aware recurring reminders are stored in UTC, but each generated instance
    should be expanded in the record's timezone before conversion back to the
    viewer's local timezone. This keeps an 8:00 AM US/Eastern series at 8:00 AM
    after the March 8, 2026 DST transition.
    """

    monkeypatch.setenv("TZ", tz_name)
    if hasattr(time, "tzset"):
        time.tzset()

    dbm = DatabaseManager(
        str(isolated_env.db_path),
        isolated_env,
        reset=True,
        auto_populate=False,
    )

    item = Item(
        env=isolated_env,
        raw=(
            "* pickleball @s 2026-01-05 8:00AM @e 90m "
            "@r w &w MO, TH @- 20260129T0800"
        ),
        final=True,
    )
    record_id = dbm.add_item(item)

    dbm.generate_datetimes_for_record(
        record_id,
        clear_existing=True,
        window=(datetime(2026, 3, 1), datetime(2026, 3, 31, 23, 59)),
    )
    dbm.conn.commit()

    (rruleset,) = dbm.cursor.execute(
        "SELECT rruleset FROM Records WHERE id=?",
        (record_id,),
    ).fetchone()
    assert "DTSTART:20260105T1300Z" in rruleset
    assert "EXDATE:20260129T1300Z" in rruleset

    rows = dbm.cursor.execute(
        "SELECT start_datetime FROM DateTimes WHERE record_id=? ORDER BY start_datetime",
        (record_id,),
    ).fetchall()
    assert rows == [
        ("20260302T0800",),
        ("20260305T0800",),
        ("20260309T0800",),
        ("20260312T0800",),
        ("20260316T0800",),
        ("20260319T0800",),
        ("20260323T0800",),
        ("20260326T0800",),
        ("20260330T0800",),
    ]

    dbm.conn.close()


def test_repetitions_fallback_handles_aware_rrules_without_datetime_rows(
    test_controller, item_factory, freeze_at
):
    item = item_factory(
        "* test for mel @s 2026-02-27 8:00AM @r w @a 0m: n",
        final=True,
    )
    record_id = test_controller.add_item(item)

    test_controller.db_manager.cursor.execute(
        "DELETE FROM DateTimes WHERE record_id = ?",
        (record_id,),
    )
    test_controller.db_manager.conn.commit()

    with freeze_at("2026-03-13 11:37:00"):
        title, lines = test_controller.get_record_repetitions(record_id, limit=3)

    assert title == "Repetitions for test for mel"
    assert lines
    assert not any("Unable to parse rruleset" in line for line in lines)
    assert lines[0].startswith("Next ")


def test_repetitions_can_start_from_selected_instance_with_datetime_rows(
    test_controller, item_factory, freeze_at
):
    item = item_factory("* weekly check-in @s 2026-02-27 8:00AM @r w", final=True)
    record_id = test_controller.add_item(item)

    test_controller.db_manager.generate_datetimes_for_record(
        record_id,
        clear_existing=True,
        window=(datetime(2026, 2, 1), datetime(2026, 4, 30, 23, 59)),
    )
    test_controller.db_manager.conn.commit()

    with freeze_at("2026-03-13 11:37:00"):
        title, lines = test_controller.get_record_repetitions(
            record_id,
            limit=3,
            instance_ts="20260327T0800",
        )

    assert title == "Repetitions for weekly check-in"
    assert lines[0] == "Next 3 occurrence(s):"
    assert lines[1] == f"  • {test_controller.fmt_user(datetime(2026, 3, 27, 8, 0))}"
    assert lines[2] == f"  • {test_controller.fmt_user(datetime(2026, 4, 3, 8, 0))}"


def test_repetitions_fallback_can_start_from_selected_instance(
    test_controller, item_factory, freeze_at
):
    item = item_factory("* weekly fallback @s 2026-02-27 8:00AM @r w", final=True)
    record_id = test_controller.add_item(item)

    test_controller.db_manager.cursor.execute(
        "DELETE FROM DateTimes WHERE record_id = ?",
        (record_id,),
    )
    test_controller.db_manager.conn.commit()

    with freeze_at("2026-03-13 11:37:00"):
        title, lines = test_controller.get_record_repetitions(
            record_id,
            limit=3,
            instance_ts="20260327T0800",
        )

    assert title == "Repetitions for weekly fallback"
    assert lines[0] == "Next 3 occurrence(s):"
    assert lines[1] == f"  • {test_controller.fmt_user(datetime(2026, 3, 27, 8, 0))}"
