"""Integration tests — exercises all 6 v0.2 features together."""

from datetime import datetime, timedelta, timezone

import pytest

from locidb.core.db import LociDB
from locidb.core.health import HealthReport


@pytest.fixture
def db(tmp_path):
    loci = LociDB(path=tmp_path / "integration.loci")
    yield loci
    loci.close()


class TestFullWorkflow:
    """End-to-end scenario: set up persona, memorize, correct, detect conflicts,
    store traces, check health, and time-travel."""

    def test_complete_v02_workflow(self, db):
        # 1. Set up persona
        db.set_persona("name", "Neetish")
        db.set_persona("role", "Founding Engineer")
        db.set_persona("communication_style", "concise")
        db.set_persona("expertise", "Python, distributed systems")

        persona = db.get_persona()
        assert persona["name"] == "Neetish"
        prompt = db.get_persona_prompt()
        assert "Neetish" in prompt

        # 2. Memorize several things
        n1 = db.memorize("Dropzi uses hub-spoke delivery model", entities=["dropzi", "delivery"])
        n2 = db.memorize("We deploy on AWS us-east-1", entities=["aws", "deployment"])
        n3 = db.memorize("Backend is Python with FastAPI", entities=["python", "fastapi"])

        # 3. Correct a memory
        n4 = db.correct(n2.id, "We deploy on GCP us-central1", entities=["gcp", "deployment"])
        assert n4.memory_type == "correction"

        # Check old memory is soft-deleted
        old = db.get(n2.id)
        assert old.deleted_at is not None

        # Check corrections list
        corrections = db.get_corrections()
        assert len(corrections) == 1
        assert "GCP" in corrections[0].content

        # 4. Store a reasoning trace
        trace_id = db.store_trace(
            query="Where do we deploy?",
            node_ids_used=[n4.id],
            response="You deploy on GCP us-central1.",
            scope="global",
            metadata={"model": "gpt-4o"},
        )

        # 5. Explain the trace
        explanation = db.explain(trace_id)
        assert "Where do we deploy?" in explanation
        assert "GCP" in explanation

        # Check traces
        all_traces = db.traces()
        assert len(all_traces) == 1

        # Check traces using a specific node
        node_traces = db.traces_using_node(n4.id)
        assert len(node_traces) == 1

        # 6. Check health
        report = db.health()
        assert isinstance(report, HealthReport)
        assert report.overall_score > 0
        assert report.dimensions["persona"] == 100
        assert report.dimensions["coverage"] > 0

        # 7. Onboarding questions (should be few since persona is filled)
        questions = db.get_onboarding_questions()
        assert len(questions) < 6  # Most persona fields are filled

        # 8. Timeline
        timeline = db.timeline()
        assert len(timeline) >= 3
        # Should be in chronological order
        for i in range(len(timeline) - 1):
            assert timeline[i].timestamp <= timeline[i + 1].timestamp

        # 9. Snapshot (current state)
        future = datetime.now(timezone.utc) + timedelta(hours=1)
        snap = db.snapshot(as_of=future)
        # n2 was soft-deleted, so only n1, n3, n4 should be in the snapshot
        snap_ids = {n.id for n in snap}
        assert n1.id in snap_ids
        assert n3.id in snap_ids
        assert n4.id in snap_ids
        assert n2.id not in snap_ids  # soft-deleted

        # 10. Diff
        past = datetime.now(timezone.utc) - timedelta(hours=1)
        diff = db.diff(past, future)
        assert len(diff["added"]) >= 3  # n1, n3, n4 added since past
        assert diff["t1_count"] == 0

        # 11. Stats still work
        stats = db.stats()
        assert stats["node_count"] >= 4  # includes soft-deleted


class TestPersonaWithRecall:
    def test_persona_prompt_in_context(self, db):
        db.set_persona("name", "Alice")
        db.memorize("Alice prefers dark mode", entities=["preferences"])
        prompt = db.get_persona_prompt()
        assert "Alice" in prompt


class TestCorrectionChain:
    def test_multiple_corrections(self, db):
        """Correct a memory, then correct the correction."""
        n1 = db.memorize("Database is MySQL", entities=["database"])
        n2 = db.correct(n1.id, "Database is PostgreSQL", entities=["database"])
        n3 = db.correct(n2.id, "Database is SQLite", entities=["database"])

        assert db.get(n1.id).deleted_at is not None
        assert db.get(n2.id).deleted_at is not None
        assert db.get(n3.id).deleted_at is None
        assert n3.memory_type == "correction"

        corrections = db.get_corrections()
        # Only non-deleted corrections should be returned
        active = [c for c in corrections if c.deleted_at is None]
        assert len(active) == 1
        assert "SQLite" in active[0].content


class TestTimeTravelWithCorrections:
    def test_snapshot_before_correction(self, db):
        n1 = db.memorize("We use Django", entities=["framework"])
        pre_correction = datetime.now(timezone.utc) + timedelta(milliseconds=50)

        import time; time.sleep(0.1)

        n2 = db.correct(n1.id, "We use FastAPI", entities=["framework"])

        # Snapshot before correction should show the original
        snap = db.snapshot(as_of=pre_correction)
        snap_ids = {n.id for n in snap}
        assert n1.id in snap_ids
        assert n2.id not in snap_ids


class TestHealthWithAllFeatures:
    def test_comprehensive_health(self, db):
        # Set persona
        db.set_persona("name", "Dev")
        db.set_persona("role", "Engineer")
        db.set_persona("expertise", "ML")
        db.set_persona("preferences", "Vim")

        # Add diverse memories
        for i in range(15):
            db.memorize(
                f"Memory about topic_{i} with details",
                entities=[f"topic_{i}", "general"],
            )

        report = db.health()
        assert report.overall_score > 50
        assert report.dimensions["persona"] == 100
        assert report.dimensions["coverage"] >= 50
        assert report.stats["node_count"] == 15
