"""Integration tests: work item lifecycle across modules.

Tests cross-module work item workflows:
- Full lifecycle: queued → in_progress → pending_review → approved/needs_revision → completed
- Session binding: work items tied to active sessions
- Priority ordering stability across bulk operations
- Count aggregation consistency through state transitions
- Delete behavior and post-delete state consistency
- Review workflow with notes across multiple status changes
"""

import pytest

from greymatter_core.db import SQLiteDatabase
from greymatter_core.sessions import SessionManager
from greymatter_core.work_items import WorkItemManager


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

@pytest.fixture
def db():
    """Shared in-memory database for work item integration tests."""
    database = SQLiteDatabase(":memory:")
    yield database
    database.close()


@pytest.fixture
def work(db):
    return WorkItemManager(db)


@pytest.fixture
def sessions(db):
    return SessionManager(db)


# ---------------------------------------------------------------------------
# 1. Full Lifecycle State Machine
# ---------------------------------------------------------------------------

class TestWorkItemLifecycle:
    """Tests the complete work item state machine through all valid transitions."""

    def test_full_happy_path_lifecycle(self, work):
        """Work item moves through entire lifecycle: queued → completed."""
        item = work.create(title="Implement OAuth2", description="Add OAuth2 login flow")
        assert item["status"] == "queued"

        # Assign work
        result = work.update_status(item["id"], "in_progress")
        assert result["status"] == "in_progress"

        # Submit for review
        result = work.update_status(item["id"], "pending_review")
        assert result["status"] == "pending_review"

        # Approve
        result = work.update_status(item["id"], "approved")
        assert result["status"] == "approved"

        # Complete
        result = work.complete(item["id"])
        assert result["status"] == "completed"
        assert result["completed_at"] is not None

    def test_revision_loop_lifecycle(self, work):
        """Work item goes through revision cycle before approval."""
        item = work.create(title="Build API gateway")

        work.update_status(item["id"], "in_progress")
        work.update_status(item["id"], "pending_review")

        # First review: needs revision
        result = work.update_review(
            item["id"], "needs_revision", "Missing rate limiting"
        )
        assert result["status"] == "needs_revision"
        assert result["review_notes"] == "Missing rate limiting"

        # Rework and resubmit
        work.update_status(item["id"], "in_progress")
        work.update_status(item["id"], "pending_review")

        # Second review: still needs work
        result = work.update_review(
            item["id"], "needs_revision", "Rate limiter missing backoff"
        )
        assert result["review_notes"] == "Rate limiter missing backoff"

        # Third attempt: approved
        work.update_status(item["id"], "in_progress")
        work.update_status(item["id"], "pending_review")
        result = work.update_review(item["id"], "approved", "LGTM")
        assert result["status"] == "approved"
        assert result["review_notes"] == "LGTM"

        # Complete
        result = work.complete(item["id"])
        assert result["status"] == "completed"

    def test_complete_sets_timestamp(self, work):
        """Completing a work item records a completed_at timestamp."""
        item = work.create(title="Write docs")
        assert item["completed_at"] is None

        result = work.complete(item["id"])
        assert result["completed_at"] is not None

    def test_complete_nonexistent_returns_none(self, work):
        """Completing a non-existent work item returns None."""
        assert work.complete("wi-does-not-exist") is None

    def test_update_status_nonexistent_returns_none(self, work):
        """Updating status of a non-existent item returns None."""
        assert work.update_status("wi-does-not-exist", "in_progress") is None


# ---------------------------------------------------------------------------
# 2. Session Binding
# ---------------------------------------------------------------------------

class TestWorkItemSessionBinding:
    """Tests work items in the context of active sessions."""

    def test_create_work_items_during_session(self, work, sessions):
        """Work items created during a session have correct project context."""
        session = sessions.start(project="greymatter")
        assert session["state"] == "active"

        item = work.create(
            title="Fix auth middleware",
            project="greymatter",
            feature="auth",
        )
        assert item["project"] == "greymatter"
        assert item["feature"] == "auth"

        sessions.end(session["id"], summary="Fixed auth")
        ended = sessions.get(session["id"])
        assert ended["ended_at"] is not None

        # Work item persists after session ends
        persisted = work.get(item["id"])
        assert persisted is not None
        assert persisted["status"] == "queued"

    def test_multiple_work_items_across_sessions(self, work, sessions):
        """Work items created in different sessions are independently managed."""
        s1 = sessions.start(project="greymatter")
        w1 = work.create(title="Task in session 1", project="greymatter")
        sessions.end(s1["id"])

        s2 = sessions.start(project="salesos")
        w2 = work.create(title="Task in session 2", project="salesos")
        sessions.end(s2["id"])

        assert work.get(w1["id"])["project"] == "greymatter"
        assert work.get(w2["id"])["project"] == "salesos"

        # Both appear in unfiltered list
        all_items = work.list()
        ids = [i["id"] for i in all_items]
        assert w1["id"] in ids
        assert w2["id"] in ids

    def test_work_item_lifecycle_spans_sessions(self, work, sessions):
        """A work item started in one session can be completed in another."""
        s1 = sessions.start(project="greymatter")
        item = work.create(title="Long running task")
        work.update_status(item["id"], "in_progress")
        sessions.end(s1["id"])

        # New session picks up the work
        s2 = sessions.start(project="greymatter")
        result = work.update_status(item["id"], "pending_review")
        assert result["status"] == "pending_review"
        sessions.end(s2["id"])

        # Review in a third session
        s3 = sessions.start(project="greymatter")
        result = work.update_review(item["id"], "approved", "Ship it")
        work.complete(item["id"])
        sessions.end(s3["id"])

        final = work.get(item["id"])
        assert final["status"] == "completed"
        assert final["review_notes"] == "Ship it"


# ---------------------------------------------------------------------------
# 3. Priority Ordering and Bulk Operations
# ---------------------------------------------------------------------------

class TestPriorityOrdering:
    """Tests priority ordering stability under various conditions."""

    def test_priority_ordering_with_many_items(self, work):
        """Priority ordering is stable with many items at different priorities."""
        priorities = [200, 50, 100, 10, 150, 75, 25]
        created = []
        for i, p in enumerate(priorities):
            item = work.create(title=f"Task {i}", priority=p)
            created.append(item)

        items = work.list()
        returned_priorities = [i["priority"] for i in items]
        assert returned_priorities == sorted(priorities)

    def test_same_priority_ordered_by_creation_time(self, work):
        """Items with identical priority are ordered by creation time (FIFO)."""
        titles = ["First", "Second", "Third"]
        for t in titles:
            work.create(title=t, priority=100)

        items = work.list()
        returned_titles = [i["title"] for i in items]
        assert returned_titles == titles

    def test_priority_preserved_through_status_changes(self, work):
        """Priority ordering is maintained after status transitions."""
        high = work.create(title="High priority", priority=10)
        low = work.create(title="Low priority", priority=200)

        # Move high to in_progress and back to queued
        work.update_status(high["id"], "in_progress")
        work.update_status(high["id"], "queued")

        queued = work.list(status="queued")
        assert queued[0]["title"] == "High priority"
        assert queued[1]["title"] == "Low priority"

    def test_list_limit(self, work):
        """List respects the limit parameter."""
        for i in range(10):
            work.create(title=f"Task {i}")

        limited = work.list(limit=3)
        assert len(limited) == 3


# ---------------------------------------------------------------------------
# 4. Count Aggregation Consistency
# ---------------------------------------------------------------------------

class TestCountAggregation:
    """Tests count accuracy through state transitions."""

    def test_count_all(self, work):
        """Total count matches number of created items."""
        for i in range(5):
            work.create(title=f"Task {i}")
        assert work.count() == 5

    def test_count_by_status(self, work):
        """Counts by status are accurate after transitions."""
        items = [work.create(title=f"Task {i}") for i in range(6)]

        work.update_status(items[0]["id"], "in_progress")
        work.update_status(items[1]["id"], "in_progress")
        work.update_status(items[2]["id"], "pending_review")
        work.complete(items[3]["id"])

        assert work.count(status="queued") == 2
        assert work.count(status="in_progress") == 2
        assert work.count(status="pending_review") == 1
        assert work.count(status="completed") == 1
        assert work.count() == 6

    def test_count_after_delete(self, work):
        """Count decreases after deletion."""
        items = [work.create(title=f"Task {i}") for i in range(3)]
        assert work.count() == 3

        work.delete(items[1]["id"])
        assert work.count() == 2

    def test_count_empty(self, work):
        """Count returns 0 for empty table or non-existent status."""
        assert work.count() == 0
        assert work.count(status="in_progress") == 0


# ---------------------------------------------------------------------------
# 5. Delete Behavior
# ---------------------------------------------------------------------------

class TestDeleteBehavior:
    """Tests delete operations and post-delete state consistency."""

    def test_delete_existing_item(self, work):
        """Deleting an existing item removes it from all queries."""
        item = work.create(title="Ephemeral task")
        item_id = item["id"]

        assert work.delete(item_id) is True
        assert work.get(item_id) is None
        assert work.list() == []

    def test_delete_nonexistent_item(self, work):
        """Deleting a non-existent item returns False."""
        assert work.delete("wi-ghost") is False

    def test_delete_does_not_affect_other_items(self, work):
        """Deleting one item leaves others intact."""
        a = work.create(title="Keep me")
        b = work.create(title="Delete me")
        c = work.create(title="Keep me too")

        work.delete(b["id"])

        remaining = work.list()
        remaining_ids = {i["id"] for i in remaining}
        assert a["id"] in remaining_ids
        assert b["id"] not in remaining_ids
        assert c["id"] in remaining_ids

    def test_delete_in_progress_item(self, work):
        """Can delete an item that is in-progress."""
        item = work.create(title="WIP task")
        work.update_status(item["id"], "in_progress")
        assert work.delete(item["id"]) is True
        assert work.get(item["id"]) is None


# ---------------------------------------------------------------------------
# 6. Review Workflow
# ---------------------------------------------------------------------------

class TestReviewWorkflow:
    """Tests the review cycle: submit → review → revise → re-review → approve."""

    def test_review_notes_overwrite_on_subsequent_reviews(self, work):
        """Each review replaces the previous review notes."""
        item = work.create(title="Implement caching")
        work.update_status(item["id"], "pending_review")

        work.update_review(item["id"], "needs_revision", "Missing TTL config")
        work.update_review(item["id"], "needs_revision", "TTL config good, but no eviction")
        result = work.update_review(item["id"], "approved", "All good now")

        assert result["review_notes"] == "All good now"
        assert result["status"] == "approved"

    def test_review_with_none_notes(self, work):
        """Review can be submitted without notes."""
        item = work.create(title="Quick fix")
        work.update_status(item["id"], "pending_review")

        result = work.update_review(item["id"], "approved", None)
        assert result["status"] == "approved"
        assert result["review_notes"] is None

    def test_review_nonexistent_returns_none(self, work):
        """Reviewing a non-existent item returns None."""
        result = work.update_review("wi-nope", "approved", "LGTM")
        assert result is None


# ---------------------------------------------------------------------------
# 7. Domain Tags and Feature Filtering
# ---------------------------------------------------------------------------

class TestDomainTagsAndFeatures:
    """Tests work item metadata: domain tags, project, feature."""

    def test_domain_tags_stored_correctly(self, work):
        """Domain tags are preserved through create and retrieve."""
        item = work.create(
            title="Security audit",
            domain_tags="security,compliance,audit",
            project="greymatter",
            feature="security",
        )
        retrieved = work.get(item["id"])
        assert retrieved["domain_tags"] == "security,compliance,audit"
        assert retrieved["project"] == "greymatter"
        assert retrieved["feature"] == "security"

    def test_short_id_derived_from_id(self, work):
        """Short ID is the last 4 characters of the full ID."""
        item = work.create(title="Test short ID")
        assert item["short_id"] == item["id"][-4:]

    def test_type_defaults_to_task(self, work):
        """Work item type defaults to 'task'."""
        item = work.create(title="Default type item")
        assert item["type"] == "task"

    def test_defaults_for_project_and_feature(self, work):
        """Project defaults to 'greymatter' and feature to 'general'."""
        item = work.create(title="Defaults test")
        assert item["project"] == "greymatter"
        assert item["feature"] == "general"
