"""
tests/test_backlog_store_jsonl.py — JSONL backend unit tests.
"""
import datetime
import json
import os
import sys
import tempfile

import pytest

sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "services", "router"))

from backlog_store import (
    BacklogItem, BacklogEvent,
    JsonlBacklogStore, _new_id, _now_iso,
)


def _make_item(**overrides) -> BacklogItem:
    base = dict(
        id=_new_id("bl"),
        created_at=_now_iso(),
        updated_at=_now_iso(),
        env="prod",
        service="gateway",
        category="arch_review",
        title="[ARCH] Review required: gateway",
        description="Test item",
        priority="P1",
        status="open",
        owner="oncall",
        due_date="2026-03-15",
        source="digest",
        dedupe_key="platform_backlog:2026-W08:prod:gateway:arch_review",
        evidence_refs={},
        tags=["auto"],
        meta={},
    )
    base.update(overrides)
    return BacklogItem.from_dict(base)


@pytest.fixture
def store(tmp_path):
    items = str(tmp_path / "items.jsonl")
    events = str(tmp_path / "events.jsonl")
    return JsonlBacklogStore(items_path=items, events_path=events)


class TestJsonlCreate:
    def test_create_and_get(self, store):
        item = _make_item()
        stored = store.create(item)
        fetched = store.get(stored.id)
        assert fetched is not None
        assert fetched.service == "gateway"

    def test_get_unknown_returns_none(self, store):
        assert store.get("no_such_id") is None

    def test_create_persists_to_file(self, store, tmp_path):
        item = _make_item()
        store.create(item)
        with open(tmp_path / "items.jsonl") as f:
            lines = [l for l in f if l.strip()]
        assert len(lines) == 1
        d = json.loads(lines[0])
        assert d["id"] == item.id


class TestJsonlDedupeKey:
    def test_get_by_dedupe_key_found(self, store):
        item = _make_item()
        store.create(item)
        found = store.get_by_dedupe_key(item.dedupe_key)
        assert found is not None
        assert found.id == item.id

    def test_get_by_dedupe_key_not_found(self, store):
        assert store.get_by_dedupe_key("nonexistent") is None

    def test_dedupe_key_uniqueness_via_upsert(self, store):
        item = _make_item()
        r1 = store.upsert(item)
        r2 = store.upsert(_make_item(
            id=_new_id("bl"),
            dedupe_key=item.dedupe_key,
            title="Updated title",
        ))
        assert r1["action"] == "created"
        assert r2["action"] == "updated"
        # Still only one dedupe_key
        items = store.list_items({"env": "prod"}, limit=100)
        keys = [it.dedupe_key for it in items]
        assert keys.count(item.dedupe_key) == 1


class TestJsonlListFilters:
    def test_list_all(self, store):
        store.create(_make_item(id=_new_id("bl"), service="svc_a", dedupe_key="k1"))
        store.create(_make_item(id=_new_id("bl"), service="svc_b", dedupe_key="k2"))
        items = store.list_items({"env": "prod"})
        assert len(items) >= 2

    def test_filter_by_service(self, store):
        store.create(_make_item(id=_new_id("bl"), service="svc_a", dedupe_key="ka"))
        store.create(_make_item(id=_new_id("bl"), service="svc_b", dedupe_key="kb"))
        results = store.list_items({"service": "svc_a"})
        assert all(it.service == "svc_a" for it in results)

    def test_filter_by_status(self, store):
        store.create(_make_item(id=_new_id("bl"), status="open", dedupe_key="d1"))
        store.create(_make_item(id=_new_id("bl"), status="done", dedupe_key="d2"))
        results = store.list_items({"status": "done"})
        assert all(it.status == "done" for it in results)

    def test_filter_by_category(self, store):
        store.create(_make_item(id=_new_id("bl"), category="arch_review", dedupe_key="c1"))
        store.create(_make_item(id=_new_id("bl"), category="refactor", dedupe_key="c2"))
        results = store.list_items({"category": "refactor"})
        assert all(it.category == "refactor" for it in results)

    def test_filter_due_before(self, store):
        store.create(_make_item(id=_new_id("bl"), due_date="2025-01-01", dedupe_key="old"))
        store.create(_make_item(id=_new_id("bl"), due_date="2027-01-01", dedupe_key="future"))
        results = store.list_items({"due_before": "2026-01-01"})
        assert all(it.due_date < "2026-01-01" for it in results if it.due_date)


class TestJsonlUpdate:
    def test_update_reflects_new_title(self, store):
        item = _make_item()
        store.create(item)
        item.title = "New Title"
        store.update(item)
        fetched = store.get(item.id)
        assert fetched.title == "New Title"


class TestJsonlEvents:
    def test_add_and_get_event(self, store):
        item = _make_item()
        store.create(item)
        ev = BacklogEvent(
            id=_new_id("ev"), item_id=item.id, ts=_now_iso(),
            type="comment", message="Test comment", actor="oncall",
        )
        store.add_event(ev)
        events = store.get_events(item.id)
        assert len(events) == 1
        assert events[0].message == "Test comment"

    def test_events_scoped_to_item(self, store):
        item1 = _make_item(id=_new_id("bl"), dedupe_key="k1")
        item2 = _make_item(id=_new_id("bl"), dedupe_key="k2")
        store.create(item1)
        store.create(item2)
        store.add_event(BacklogEvent(id=_new_id("ev"), item_id=item1.id,
                                     ts=_now_iso(), type="comment", message="A", actor="a"))
        store.add_event(BacklogEvent(id=_new_id("ev"), item_id=item2.id,
                                     ts=_now_iso(), type="comment", message="B", actor="b"))
        ev1 = store.get_events(item1.id)
        assert all(e.item_id == item1.id for e in ev1)


class TestJsonlCleanup:
    def test_cleanup_removes_old_done_items(self, store):
        old_ts = (datetime.datetime.utcnow() - datetime.timedelta(days=200)).isoformat()
        item = _make_item(status="done", updated_at=old_ts, dedupe_key="old_done")
        store.create(item)
        deleted = store.cleanup(retention_days=180)
        assert deleted == 1
        assert store.get(item.id) is None

    def test_cleanup_keeps_open_items(self, store):
        item = _make_item(status="open", dedupe_key="open_item")
        store.create(item)
        deleted = store.cleanup(retention_days=1)
        assert deleted == 0
        assert store.get(item.id) is not None

    def test_cleanup_keeps_recent_done(self, store):
        item = _make_item(status="done", dedupe_key="recent_done")
        store.create(item)
        deleted = store.cleanup(retention_days=180)
        assert deleted == 0


class TestJsonlDashboard:
    def test_dashboard_structure(self, store):
        store.create(_make_item(id=_new_id("bl"), dedupe_key="d1", status="open"))
        store.create(_make_item(id=_new_id("bl"), dedupe_key="d2", status="done"))
        dash = store.dashboard(env="prod")
        assert "total" in dash
        assert "status_counts" in dash
        assert "priority_counts" in dash
        assert "overdue" in dash
        assert "top_services" in dash

    def test_dashboard_overdue(self, store):
        past_due = "2020-01-01"
        store.create(_make_item(id=_new_id("bl"), due_date=past_due,
                                status="open", dedupe_key="overdue_item"))
        dash = store.dashboard(env="prod")
        assert dash["overdue_count"] >= 1
