"""ApprovalMixin — plan approval, plan thread replies, and inline corrections."""
from __future__ import annotations

import contextlib
import logging
from typing import Any

from dobby.slack.state import (
    _PENDING,
    _PLAN_THREADS,
    _PendingBatch,
    _PendingConfirmation,
    _save_plan_threads,
    _strip_mention,
    register_task_thread,
)

logger = logging.getLogger(__name__)


class ApprovalMixin:
    """Plan approval, plan thread continuation, and inline correction methods."""

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

    async def _approve_plan(self, plan: dict, channel: str, thread_ts: str, client: Any) -> None:
        """Create an execution task from an approved plan."""
        task = plan["task"]
        result = plan["result"]
        session_id = plan.get("session_id")

        # Create execution task that resumes the planning session
        payload = {
            "project": task.get("project"),
            "description": (
                f"Execute the plan from task {task.get('id')}. "
                f"The plan was: {result.get('summary', '')[:500]}. "
                f"Now implement it fully."
            ),
            "mode": "quick",
            "priority": task.get("priority", "normal"),
            "backend": task.get("backend", "claude"),
            "target": task.get("target", "local"),
        }
        if session_id:
            payload["session_id"] = session_id

        try:
            new_task = await self._client_wrapper.post("/tasks", json=payload)
            new_id = new_task.get("id", "?")
            register_task_thread(new_id, channel, thread_ts)
            await client.chat_postMessage(
                channel=channel, thread_ts=thread_ts,
                text=(
                    f":rocket: Plan approved! Execution task `{new_id}` queued."
                    " I'll post updates here."
                ),
            )
        except Exception as exc:
            await client.chat_postMessage(
                channel=channel, thread_ts=thread_ts,
                text=f":warning: Failed to create execution task: `{exc}`",
            )

    async def _handle_plan_reply(
        self, thread_ts: str, text: str, event: dict, say: Any, client: Any
    ) -> None:
        """Handle a reply in a plan conversation thread -- resume the session."""
        plan_info = _PLAN_THREADS.get(thread_ts)
        if not plan_info:
            return

        channel = event.get("channel", "")
        session_id = plan_info.get("session_id")
        clean = _strip_mention(text).strip() if text else text

        # Check if the user is approving the plan
        approval_phrases = (
            "approve", "approved", "looks good", "lgtm", "ship it", "go ahead", "execute", "do it"
        )
        if clean.lower().strip() in approval_phrases:
            await self._approve_plan_from_thread(plan_info, channel, thread_ts, client)
            return

        # Otherwise, create a resume task to continue the planning conversation
        description = (
            f"{clean}\n\n"
            "Continue the planning conversation. The user replied in Slack with the above. "
            "Respond to their feedback, update the plan if needed, "
            "and ask any follow-up questions. "
            "Do NOT output a DOBBY_RESULT block \u2014 just respond conversationally."
        )

        payload = {
            "project": plan_info.get("project"),
            "description": description,
            "mode": "planned",
            "priority": "normal",
            "backend": plan_info.get("backend", "claude"),
            "target": plan_info.get("target", "local"),
        }
        if session_id:
            payload["session_id"] = session_id

        try:
            # Post typing indicator
            typing_ts = None
            if client:
                with contextlib.suppress(Exception):
                    typing_resp = await client.chat_postMessage(
                        channel=channel, thread_ts=thread_ts,
                        text=":hourglass_flowing_sand: _Thinking about your feedback..._",
                    )
                    typing_ts = (
                        typing_resp.get("ts") if isinstance(typing_resp, dict)
                        else getattr(typing_resp, "data", {}).get("ts")
                    )

            new_task = await self._client_wrapper.post("/tasks", json=payload)
            new_id = new_task.get("id", "?")
            # Register so the completion posts back to this same thread
            register_task_thread(new_id, channel, thread_ts)

            # Remove typing indicator -- the task result will post to the thread
            if client and typing_ts:
                with contextlib.suppress(Exception):
                    await client.chat_delete(channel=channel, ts=typing_ts)

        except Exception as exc:
            logger.warning("Plan reply task creation failed: %s", exc)
            if client:
                await client.chat_postMessage(
                    channel=channel, thread_ts=thread_ts,
                    text=f":warning: Failed to create plan reply task: `{exc}`",
                )

    async def _approve_plan_from_thread(
        self, plan_info: dict, channel: str, thread_ts: str, client: Any
    ) -> None:
        """Approve a plan from a thread reply and create an execution task."""
        session_id = plan_info.get("session_id")

        payload = {
            "project": plan_info.get("project"),
            "description": (
                "Execute the plan from the planning conversation. "
                "The user has approved the plan \u2014 now implement it fully."
            ),
            "mode": "quick",
            "priority": "normal",
            "backend": plan_info.get("backend", "claude"),
            "target": plan_info.get("target", "local"),
        }
        if session_id:
            payload["session_id"] = session_id

        try:
            new_task = await self._client_wrapper.post("/tasks", json=payload)
            new_id = new_task.get("id", "?")
            register_task_thread(new_id, channel, thread_ts)
            # Remove from plan threads -- conversation is done
            _PLAN_THREADS.pop(thread_ts, None)
            _save_plan_threads()
            await client.chat_postMessage(
                channel=channel, thread_ts=thread_ts,
                text=(
                    f":rocket: Plan approved! Execution task `{new_id}` queued."
                    " I'll post updates here."
                ),
            )
        except Exception as exc:
            await client.chat_postMessage(
                channel=channel, thread_ts=thread_ts,
                text=f":warning: Failed to create execution task: `{exc}`",
            )

    async def _handle_correction(
        self, thread_ts: str, text: str, event: dict, _say: Any, client: Any
    ) -> None:
        """Apply an inline correction to a pending task."""
        pending = _PENDING.get(thread_ts)
        if pending is None:
            return

        channel: str = event.get("channel", "")

        if isinstance(pending, _PendingConfirmation):
            updated = await self._parser.apply_correction(pending.parsed, text, pending.projects)
            pending.parsed = updated
            preview = self._format_confirmation_preview(updated, updated=True)
            await client.chat_postMessage(
                channel=channel, thread_ts=thread_ts,
                text=preview,
            )
        elif isinstance(pending, _PendingBatch):
            # For batch, apply correction to all tasks
            new_tasks = []
            for task in pending.tasks:
                updated = await self._parser.apply_correction(task, text, pending.projects)
                new_tasks.append(updated)
            pending.tasks = new_tasks
            preview = self._format_batch_preview(new_tasks, updated=True)
            await client.chat_postMessage(
                channel=channel, thread_ts=thread_ts,
                text=preview,
            )
