from __future__ import annotations

from datetime import date

import pytest

from republic.core.errors import ErrorKind
from republic.core.results import ErrorPayload
from republic.tape.context import LAST_ANCHOR, TapeContext
from republic.tape.entries import TapeEntry
from republic.tape.manager import TapeManager
from republic.tape.query import TapeQuery
from republic.tape.store import InMemoryTapeStore


def _seed_entries() -> list[TapeEntry]:
    return [
        TapeEntry.message({"role": "user", "content": "before"}),
        TapeEntry.anchor("a1"),
        TapeEntry.message({"role": "user", "content": "task 1"}),
        TapeEntry.message({"role": "assistant", "content": "answer 1"}),
        TapeEntry.anchor("a2"),
        TapeEntry.message({"role": "user", "content": "task 2"}),
    ]


@pytest.fixture
def manager() -> TapeManager:
    store = InMemoryTapeStore()
    for entry in _seed_entries():
        store.append("test_tape", entry)
    return TapeManager(store=store)


def test_build_messages_uses_last_anchor_slice(manager) -> None:
    messages = manager.read_messages("test_tape", context=TapeContext(anchor=LAST_ANCHOR))
    assert [message["content"] for message in messages] == ["task 2"]


def test_build_messages_reports_missing_anchor(manager) -> None:
    with pytest.raises(ErrorPayload) as exc_info:
        manager.read_messages("test_tape", context=TapeContext(anchor="missing"))
    assert exc_info.value.kind == ErrorKind.NOT_FOUND


def test_query_between_anchors_and_limit() -> None:
    store = InMemoryTapeStore()
    tape = "session"

    for entry in _seed_entries():
        store.append(tape, entry)

    entries = list(TapeQuery(tape=tape, store=store).between_anchors("a1", "a2").kinds("message").limit(1).all())
    assert len(entries) == 1
    assert entries[0].payload["content"] == "task 1"


def test_query_text_matches_payload_and_meta() -> None:
    store = InMemoryTapeStore()
    tape = "searchable"

    store.append(tape, TapeEntry.message({"role": "user", "content": "Database timeout on checkout"}, scope="db"))
    store.append(tape, TapeEntry.event("run", {"status": "ok"}, scope="system"))

    entries = list(TapeQuery(tape=tape, store=store).query("timeout").all())
    assert len(entries) == 1
    assert entries[0].kind == "message"

    meta_entries = list(TapeQuery(tape=tape, store=store).query("system").all())
    assert len(meta_entries) == 1
    assert meta_entries[0].kind == "event"


def test_query_between_dates_filters_inclusive_range() -> None:
    store = InMemoryTapeStore()
    tape = "dated"

    store.append(
        tape,
        TapeEntry(
            id=0,
            kind="message",
            payload={"role": "user", "content": "before"},
            date="2026-03-01T08:00:00+00:00",
        ),
    )
    store.append(
        tape,
        TapeEntry(
            id=0,
            kind="message",
            payload={"role": "user", "content": "during"},
            date="2026-03-02T09:30:00+00:00",
        ),
    )
    store.append(
        tape,
        TapeEntry(
            id=0,
            kind="message",
            payload={"role": "user", "content": "after"},
            date="2026-03-04T18:45:00+00:00",
        ),
    )

    entries = list(TapeQuery(tape=tape, store=store).between_dates(date(2026, 3, 2), "2026-03-03").all())
    assert [entry.payload["content"] for entry in entries] == ["during"]


def test_query_combines_anchor_date_and_text_filters() -> None:
    store = InMemoryTapeStore()
    tape = "combined"

    store.append(
        tape,
        TapeEntry(
            id=0,
            kind="anchor",
            payload={"name": "a1"},
            date="2026-03-01T00:00:00+00:00",
        ),
    )
    store.append(
        tape,
        TapeEntry(
            id=0,
            kind="message",
            payload={"role": "user", "content": "old timeout"},
            date="2026-03-01T12:00:00+00:00",
        ),
    )
    store.append(
        tape,
        TapeEntry(
            id=0,
            kind="anchor",
            payload={"name": "a2"},
            date="2026-03-02T00:00:00+00:00",
        ),
    )
    store.append(
        tape,
        TapeEntry(
            id=0,
            kind="message",
            payload={"role": "user", "content": "new timeout"},
            meta={"source": "ops"},
            date="2026-03-02T12:00:00+00:00",
        ),
    )
    store.append(
        tape,
        TapeEntry(
            id=0,
            kind="message",
            payload={"role": "user", "content": "new success"},
            meta={"source": "ops"},
            date="2026-03-03T12:00:00+00:00",
        ),
    )

    entries = list(
        TapeQuery(tape=tape, store=store)
        .after_anchor("a2")
        .between_dates("2026-03-02", "2026-03-02")
        .query("timeout")
        .all()
    )
    assert [entry.payload["content"] for entry in entries] == ["new timeout"]
