"""SlashCommandMixin — /dobby slash command handling and todo management."""
from __future__ import annotations

import logging
from typing import Any

logger = logging.getLogger(__name__)


class SlashCommandMixin:
    """Slash command and todo sub-command methods."""

    # These attributes are provided by DobbySlackBot.__init__:
    #   self._client_wrapper

    async def _handle_slash_command(self, ack: Any, command: dict, say: Any) -> None:
        """Handle /dobby slash command with subcommands."""
        await ack()
        text = (command.get("text") or "").strip()
        lower = text.lower()

        if not text or lower == "help":
            await say(
                "*`/dobby` commands:*\n"
                "\u2022 `/dobby status` \u2014 quick task overview\n"
                "\u2022 `/dobby summary` \u2014 AI-generated summary\n"
                "\u2022 `/dobby cost` \u2014 cost breakdown by project and time range\n"
                "\u2022 `/dobby projects` \u2014 list projects with spokes\n"
                "\u2022 `/dobby running` \u2014 currently running tasks\n"
                "\u2022 `/dobby recall [query]` \u2014 search memories\n"
                "\u2022 `/dobby todos` \u2014 list open todos\n"
                "\u2022 `/dobby todo add \"desc\" [--project X] [--due DATE] [--priority P]`"
                " \u2014 add a todo\n"
                "\u2022 `/dobby todo done <id>` \u2014 mark a todo done\n"
                "\u2022 `/dobby help` \u2014 this message"
            )
            return

        if lower == "cost" or lower.startswith("cost "):
            project = text[len("cost"):].strip() or None
            await self._handle_cost(say, project=project)
        elif lower == "status" or lower.startswith("status "):
            hours, project = self._parse_time_and_project(text, ["status"], default_hours=4)
            await self._handle_status(say, hours=hours, project=project)
        elif lower == "summary" or lower.startswith("summary "):
            hours, project = self._parse_time_and_project(text, ["summary"], default_hours=12)
            channel_id = command.get("channel_id") or command.get("channel")
            await self._handle_summary(say, hours=hours, project=project, channel_id=channel_id)
        elif lower in ("projects", "project list"):
            await self._handle_projects(say)
        elif lower in ("running", "running?"):
            await self._handle_running_query(say)
        elif lower == "todos" or lower.startswith("todos "):
            await self._handle_todos_list(say, text)
        elif lower.startswith("todo "):
            await self._handle_todo_command(say, text)
        elif lower.startswith("recall"):
            query = text[len("recall"):].strip() or None
            try:
                memories = await self._client_wrapper.get(
                    "/memories", params={"q": query} if query else None
                )
                if not memories:
                    await say(":brain: No memories found.")
                else:
                    lines = [":brain: *Memories:*\n"]
                    for m in (memories if isinstance(memories, list) else []):
                        lines.append(
                            f"\u2022 `{m.get('key','')}` [{m.get('type','')}]:"
                            f" {m.get('value','')[:100]}"
                        )
                    await say("\n".join(lines))
            except Exception as exc:
                await say(f":warning: Failed to recall: `{exc}`")
        else:
            await say(f":thinking_face: Unknown subcommand: `{text}`. Try `/dobby help`")

    # ------------------------------------------------------------------
    # Todos
    # ------------------------------------------------------------------

    async def _handle_todos_list(self, say: Any, text: str) -> None:
        """List open todos, optionally filtered by project. `/dobby todos [project]`"""
        parts = text.split(None, 1)
        project = parts[1].strip() if len(parts) > 1 else None

        try:
            params: dict[str, str] = {"status": "open"}
            if project:
                params["project"] = project
            todos = await self._client_wrapper.get("/todos", params=params)
        except Exception as exc:
            await say(f":warning: Could not fetch todos: `{exc}`")
            return

        if not isinstance(todos, list):
            todos = todos.get("items", []) if isinstance(todos, dict) else []

        if not todos:
            await say(":clipboard: No open todos." + (f" (project: {project})" if project else ""))
            return

        # Group by project
        by_project: dict[str, list] = {}
        for t in todos:
            proj = t.get("project") or "(no project)"
            by_project.setdefault(proj, []).append(t)

        lines = [":clipboard: *Open Todos*\n"]
        for proj, items in sorted(by_project.items()):
            lines.append(f"*{proj}*")
            for t in items:
                priority = t.get("priority", "normal")
                p_emoji = {
                    "urgent": ":rotating_light:", "high": ":red_circle:",
                    "normal": "", "low": ":white_circle:",
                }.get(priority, "")
                due = t.get("due_date")
                due_str = f" (due {due[:10]})" if due else ""
                tags = t.get("tags", [])
                tag_str = f" [{', '.join(tags)}]" if tags else ""
                lines.append(
                    f"  {p_emoji} `{t['id'][:20]}` {t.get('description', '')[:80]}"
                    f"{due_str}{tag_str}"
                )
            lines.append("")

        await say("\n".join(lines))

    async def _handle_todo_command(self, say: Any, text: str) -> None:
        """Handle `/dobby todo add|done|cancel ...`"""
        # Strip "todo " prefix
        rest = text[len("todo"):].strip()
        lower = rest.lower()

        if lower.startswith("add "):
            await self._handle_todo_add(say, rest[len("add"):].strip())
        elif lower.startswith("done "):
            todo_id = rest[len("done"):].strip()
            await self._handle_todo_status_change(say, todo_id, "done")
        elif lower.startswith("cancel "):
            todo_id = rest[len("cancel"):].strip()
            await self._handle_todo_status_change(say, todo_id, "cancelled")
        else:
            await say(
                ":thinking_face: Unknown todo command."
                " Try: `todo add`, `todo done <id>`, `todo cancel <id>`"
            )

    async def _handle_todo_add(self, say: Any, text: str) -> None:
        """Parse and create a todo from slash command text.

        Supports: `"description" --project X --due DATE --priority P --tags a,b`
        """
        import shlex
        try:
            tokens = shlex.split(text)
        except ValueError:
            tokens = text.split()

        if not tokens:
            await say(
                ":warning: Usage: `/dobby todo add \"description\""
                " [--project X] [--due DATE] [--priority P]`"
            )
            return

        body: dict = {}
        description_parts: list[str] = []
        i = 0
        while i < len(tokens):
            tok = tokens[i]
            if tok == "--project" and i + 1 < len(tokens):
                body["project"] = tokens[i + 1]
                i += 2
            elif tok == "--due" and i + 1 < len(tokens):
                body["due_date"] = tokens[i + 1]
                i += 2
            elif tok == "--priority" and i + 1 < len(tokens):
                body["priority"] = tokens[i + 1]
                i += 2
            elif tok == "--tags" and i + 1 < len(tokens):
                body["tags"] = [t.strip() for t in tokens[i + 1].split(",")]
                i += 2
            else:
                description_parts.append(tok)
                i += 1

        body["description"] = " ".join(description_parts)
        if not body["description"]:
            await say(":warning: Please provide a description for the todo.")
            return

        try:
            result = await self._client_wrapper.post("/todos", json=body)
            todo_id = result.get("id", "?") if isinstance(result, dict) else "?"
            await say(f":white_check_mark: Todo created: `{todo_id}` \u2014 {body['description']}")
        except Exception as exc:
            await say(f":warning: Failed to create todo: `{exc}`")

    async def _handle_todo_status_change(self, say: Any, todo_id: str, status: str) -> None:
        """Mark a todo as done or cancelled."""
        if not todo_id:
            await say(":warning: Please provide a todo ID.")
            return
        try:
            result = await self._client_wrapper.patch(f"/todos/{todo_id}", json={"status": status})
            desc = result.get("description", "") if isinstance(result, dict) else ""
            emoji = ":white_check_mark:" if status == "done" else ":no_entry_sign:"
            await say(f"{emoji} Todo `{todo_id}` marked {status}. {desc[:60]}")
        except Exception as exc:
            await say(f":warning: Failed to update todo: `{exc}`")
