"""Work item management for GreyMatter.

Simple local task list with priority ordering and status tracking.
No distributed locking -- single user, single process.
"""
from __future__ import annotations

from typing import Any

from .db import Database
from .ids import generate_work_item_id

# Valid dispatch modes that can be recorded on a work item.
VALID_DISPATCH_MODES = {"autonomous", "supervised", "planned"}


class WorkItemManager:
    """Manages work items: create, update, complete, list."""

    def __init__(self, db: Database):
        self.db = db

    def create(
        self,
        title: str,
        description: str | None = None,
        priority: int = 100,
        domain_tags: str | None = None,
        project: str = "greymatter",
        feature: str = "general",
        dispatch_mode: str | None = None,
    ) -> dict[str, Any]:
        """Create a new work item, mode-aware.

        When *dispatch_mode* is ``"planned"``, the item is created directly
        in ``pending_approval`` status so it enters the approval gate
        without a separate manual step.  For all other modes the initial
        status is ``"queued"`` (the historical default).

        Args:
            title: Short descriptive title for the work item.
            description: Detailed description of the work.
            priority: Priority value (lower = higher priority).
            domain_tags: Comma-separated domain tags.
            project: Project this work item belongs to.
            feature: Feature area within the project.
            dispatch_mode: The active dispatch mode at creation time
                (``"autonomous"``, ``"supervised"``, or ``"planned"``).
                Stored on the item for audit/filtering.  ``None`` means
                the caller didn't supply mode info (legacy path).

        Returns:
            The created work item dict.
        """
        # Normalise and validate mode
        mode_value: str | None = None
        if dispatch_mode is not None:
            mode_value = dispatch_mode.strip().lower() or None
            if mode_value and mode_value not in VALID_DISPATCH_MODES:
                mode_value = None  # don't store invalid values

        # In planned mode, items start at the approval gate
        initial_status = "pending_approval" if mode_value == "planned" else "queued"

        item_id = generate_work_item_id()
        short_id = item_id[-4:]
        self.db.execute(
            """INSERT INTO work_items (id, title, description, priority, domain_tags,
               project, feature, type, short_id, status, dispatch_mode)
               VALUES (?, ?, ?, ?, ?, ?, ?, 'task', ?, ?, ?)""",
            (item_id, title, description, priority, domain_tags,
             project, feature, short_id, initial_status, mode_value),
        )
        return self.get(item_id)

    def get(self, item_id: str) -> dict[str, Any] | None:
        """Retrieve a work item by ID.

        Args:
            item_id: The work item's unique identifier.

        Returns:
            The work item dict, or None if not found.
        """
        return self.db.execute_one("SELECT * FROM work_items WHERE id = ?", (item_id,))

    def list(
        self,
        status: str | None = None,
        dispatch_mode: str | None = None,
        limit: int = 50,
    ) -> list[dict[str, Any]]:
        """List work items ordered by priority then creation date.

        Args:
            status: Filter to items with this status (e.g. 'queued', 'in_progress').
            dispatch_mode: Filter to items created under this dispatch mode.
            limit: Maximum number of items to return.

        Returns:
            List of work item dicts matching the filters.
        """
        query = "SELECT * FROM work_items WHERE 1=1"
        params: list = []
        if status:
            query += " AND status = ?"
            params.append(status)
        if dispatch_mode:
            query += " AND dispatch_mode = ?"
            params.append(dispatch_mode)
        query += " ORDER BY priority ASC, created_at ASC LIMIT ?"
        params.append(limit)
        return self.db.execute(query, tuple(params))

    def update_status(self, item_id: str, status: str) -> dict[str, Any] | None:
        """Update a work item's status.

        Args:
            item_id: The work item to update.
            status: New status value (e.g. 'in_progress', 'pending_review').

        Returns:
            The updated work item dict, or None if not found.
        """
        if self.get(item_id) is None:
            return None
        self.db.execute("UPDATE work_items SET status = ? WHERE id = ?", (status, item_id))
        return self.get(item_id)

    def update_review(
        self, item_id: str, status: str, review_notes: str | None = None
    ) -> dict[str, Any] | None:
        """Update work item status with review notes."""
        if self.get(item_id) is None:
            return None
        self.db.execute(
            "UPDATE work_items SET status = ?, review_notes = ? WHERE id = ?",
            (status, review_notes, item_id),
        )
        return self.get(item_id)

    def complete(
        self,
        item_id: str,
        result_summary: str | None = None,
    ) -> dict[str, Any] | None:
        """Mark a work item as completed and store its result summary.

        Args:
            item_id: The work item to complete.
            result_summary: Optional summary of completion results (e.g.,
                ``"3 files changed, commit abc1234"`` or failure details).

        Returns:
            The completed work item dict, or None if not found.
        """
        if self.get(item_id) is None:
            return None
        self.db.execute(
            "UPDATE work_items SET status = 'completed', completed_at = datetime('now'), "
            "result_summary = ? WHERE id = ?",
            (result_summary, item_id),
        )
        return self.get(item_id)

    def completed_since(self, hours: int = 24, limit: int = 100) -> list[dict[str, Any]]:
        """Return work items completed within the last *hours* hours.

        Uses SQLite's datetime arithmetic to find items whose ``completed_at``
        timestamp falls within the requested window.  Results are ordered most-
        recent first.

        Args:
            hours: Look-back window in hours (default 24).
            limit: Maximum number of items to return.

        Returns:
            List of work item dicts completed within the window.
        """
        return self.db.execute(
            """SELECT * FROM work_items
               WHERE status = 'completed'
               AND completed_at >= datetime('now', ?)
               ORDER BY completed_at DESC LIMIT ?""",
            (f"-{hours} hours", limit),
        )

    def count(self, status: str | None = None) -> int:
        """Count work items, optionally filtered by status."""
        where = "WHERE 1=1"
        params: list = []
        if status:
            where += " AND status = ?"
            params.append(status)
        row = self.db.execute_one(
            f"SELECT COUNT(*) as total FROM work_items {where}", tuple(params)
        )
        return row["total"] if row else 0

    def delete(self, item_id: str) -> bool:
        if self.get(item_id) is None:
            return False
        self.db.execute("DELETE FROM work_items WHERE id = ?", (item_id,))
        return True

    # ─── Plan Approval Gate ──────────────────────────────────

    def mark_pending_approval(self, item_id: str) -> dict[str, Any] | None:
        """Mark a queued work item as pending_approval (planned mode gate).

        Items in this state are claimed by the daemon but will NOT have an
        agent spawned until explicitly approved via ``approve()``.

        Returns:
            The updated work item dict, or None if not found.
        """
        item = self.get(item_id)
        if item is None:
            return None
        self.db.execute(
            "UPDATE work_items SET status = 'pending_approval' WHERE id = ?",
            (item_id,),
        )
        return self.get(item_id)

    def approve(self, item_id: str) -> dict[str, Any] | None:
        """Approve a pending_approval work item for agent dispatch.

        Transitions the item from ``pending_approval`` to ``approved_for_dispatch``,
        signalling the daemon that this item is cleared to spawn an agent.

        Returns:
            The updated work item dict, or None if not found or wrong status.
        """
        item = self.get(item_id)
        if item is None:
            return None
        if item.get("status") != "pending_approval":
            return None
        self.db.execute(
            "UPDATE work_items SET status = 'approved_for_dispatch' WHERE id = ?",
            (item_id,),
        )
        return self.get(item_id)

    def approve_all(self) -> list[dict[str, Any]]:
        """Approve all pending_approval work items for dispatch.

        Returns:
            List of updated work item dicts.
        """
        pending = self.db.execute(
            "SELECT id FROM work_items WHERE status = 'pending_approval' "
            "ORDER BY priority ASC, created_at ASC"
        )
        approved = []
        for row in pending:
            result = self.approve(row["id"])
            if result:
                approved.append(result)
        return approved

    def list_pending_approval(self) -> list[dict[str, Any]]:
        """List all work items awaiting approval.

        Returns:
            List of work item dicts with status ``pending_approval``.
        """
        return self.db.execute(
            "SELECT * FROM work_items WHERE status = 'pending_approval' "
            "ORDER BY priority ASC, created_at ASC"
        )
