"""Slack interactions and slash command endpoints."""

from __future__ import annotations

import asyncio
import json
import logging

from aiohttp import web

from dobby.models import TaskStatus

logger = logging.getLogger(__name__)


# ---------------------------------------------------------------------------
# Slack interactions endpoint
# ---------------------------------------------------------------------------

async def slack_interactions(request: web.Request) -> web.Response:
    """Handle Slack Block Kit interactive payloads (buttons, menus).

    Slack sends these as application/x-www-form-urlencoded with a 'payload' field
    containing the JSON interaction data.
    """
    try:
        post_data = await request.post()
        raw_payload = post_data.get("payload", "")
        if not raw_payload:
            raise web.HTTPBadRequest(reason="Missing payload field")
        payload = json.loads(raw_payload)
    except (json.JSONDecodeError, TypeError) as exc:
        raise web.HTTPBadRequest(reason=f"Invalid payload: {exc}") from exc

    slack_bot = request.app.get("slack_bot")
    if slack_bot is None:
        logger.warning("Slack interaction received but no bot instance registered")
        return web.json_response({"ok": True})

    # Process asynchronously so we respond within Slack's 3s timeout
    asyncio.ensure_future(slack_bot.handle_interaction(payload))

    return web.json_response({"ok": True})


# ---------------------------------------------------------------------------
# Slack slash command  (/dobby)
# ---------------------------------------------------------------------------

async def slack_command(request: web.Request) -> web.Response:
    """Handle Slack /dobby slash command.

    Slack sends application/x-www-form-urlencoded with fields:
    token, command, text, user_id, channel_id, response_url, etc.

    We return an immediate response (<3s) and use response_url for async results.
    """
    post_data = await request.post()
    text = (post_data.get("text") or "").strip()
    response_url = post_data.get("response_url", "")

    # Parse subcommand from text
    parts = text.split(None, 1)
    subcommand = parts[0].lower() if parts else "help"
    args = parts[1] if len(parts) > 1 else ""

    # Dispatch — return fast, compute async
    if subcommand in ("status", "summary", "projects", "help"):
        # These can respond inline quickly
        pass
    elif subcommand == "add" and not args:
        return _slack_response("Usage: `/dobby add <project> <description>`")
    elif subcommand == "recall" and not args:
        return _slack_response("Usage: `/dobby recall <search query>`")

    # For anything that needs store access, run async and reply via response_url
    store = request.app["store"]
    asyncio.ensure_future(
        _handle_slack_subcommand(store, subcommand, args, response_url)
    )

    # Immediate acknowledgement
    return _slack_response(f"Working on `/dobby {subcommand}`...")


def _slack_response(text: str, in_channel: bool = False) -> web.Response:
    """Build a Slack slash command response."""
    return web.json_response({
        "response_type": "in_channel" if in_channel else "ephemeral",
        "text": text,
    })


async def _handle_slack_subcommand(
    store, subcommand: str, args: str, response_url: str
) -> None:
    """Process slash command async and post result to response_url."""
    try:
        text = await _run_slack_subcommand(store, subcommand, args)
    except Exception as exc:
        logger.exception("Slack command error: /dobby %s", subcommand)
        text = f"Error: {exc}"

    if not response_url:
        return

    import aiohttp
    async with aiohttp.ClientSession() as session:
        await session.post(response_url, json={
            "response_type": "ephemeral",
            "text": text,
        })


async def _run_slack_subcommand(store, subcommand: str, args: str) -> str:
    """Execute a subcommand and return the response text."""
    if subcommand == "status":
        return await _slack_status(store)
    elif subcommand == "summary":
        return await _slack_summary(store)
    elif subcommand == "projects":
        return await _slack_projects(store)
    elif subcommand == "add":
        return await _slack_add(store, args)
    elif subcommand == "recall":
        return await _slack_recall(store, args)
    elif subcommand == "help":
        return _slack_help()
    else:
        return f"Unknown subcommand: `{subcommand}`\n\n{_slack_help()}"


async def _slack_status(store) -> str:
    """Build status overview."""
    all_tasks = await store.list_tasks()
    all_results = await store.list_results()
    counts: dict[str, int] = {s.value: 0 for s in TaskStatus}
    for task in all_tasks:
        counts[task.status.value] += 1
    total_cost = sum(
        (r.cost or {}).get("cost_usd", 0.0) for r in all_results
    )

    lines = [
        "*Dobby Status*",
        f"> Pending: {counts.get('pending', 0)} | Running: {counts.get('running', 0)} "
        f"| Completed: {counts.get('completed', 0)} | Failed: {counts.get('failed', 0)}",
        f"> Total cost: ${total_cost:.2f}",
    ]

    active = [t for t in all_tasks if t.status in (TaskStatus.PENDING, TaskStatus.RUNNING)]
    if active:
        lines.append("\n*Active Tasks:*")
        for t in active[:10]:
            desc = (t.description or "")[:60]
            lines.append(f"• `{t.id}` [{t.project}] {t.status.value} — {desc}")
    else:
        lines.append("\n_No active tasks._")

    return "\n".join(lines)


async def _slack_summary(store) -> str:
    """Build a narrative summary of recent activity."""
    all_tasks = await store.list_tasks()
    results = await store.list_results(last_n=10)
    counts: dict[str, int] = {s.value: 0 for s in TaskStatus}
    for task in all_tasks:
        counts[task.status.value] += 1
    total_cost = sum(
        (r.cost or {}).get("cost_usd", 0.0) for r in await store.list_results()
    )

    lines = [
        "*Dobby Summary*",
        f"Total tasks: {len(all_tasks)} | "
        f"Completed: {counts.get('completed', 0)} | "
        f"Failed: {counts.get('failed', 0)} | "
        f"Running: {counts.get('running', 0)} | "
        f"Pending: {counts.get('pending', 0)}",
        f"Total cost: ${total_cost:.2f}",
    ]

    if results:
        lines.append("\n*Recent Results:*")
        for r in results[:5]:
            summary = (r.summary or "No summary")[:80]
            lines.append(f"• `{r.task_id}` [{r.project}] — {summary}")

    running = [t for t in all_tasks if t.status == TaskStatus.RUNNING]
    if running:
        lines.append("\n*Currently Running:*")
        for t in running:
            desc = (t.description or "")[:60]
            lines.append(f"• `{t.id}` [{t.project}] — {desc}")

    return "\n".join(lines)


async def _slack_projects(store) -> str:
    """List registered projects."""
    projects = await store.list_projects()
    if not projects:
        return "_No projects registered._"

    lines = ["*Registered Projects:*"]
    for p in projects:
        ctx = f" — {p.context}" if p.context else ""
        lines.append(f"• *{p.name}*: `{p.repo}`{ctx}")
    lines.append(f"\n_{len(projects)} project(s)_")
    return "\n".join(lines)


async def _slack_add(store, args: str) -> str:
    """Quick task creation: /dobby add <project> <description>."""
    from dobby.core.api.tasks import _task_from_body

    parts = args.split(None, 1)
    if len(parts) < 2:
        return "Usage: `/dobby add <project> <description>`"
    project_name, description = parts[0], parts[1]
    task = _task_from_body({"project": project_name, "description": description})
    await store.create_task(task)
    return f"Task queued: `{task.id}`\n• Project: {project_name}\n• {description[:100]}"


async def _slack_recall(store, query: str) -> str:
    """Search memories."""
    memories = await store.search_memories(query=query)
    if not memories:
        return f"_No memories matching \"{query}\"._"

    lines = [f"*Memories matching \"{query}\":*"]
    for m in memories[:10]:
        val = (m.get("value") or "")[:120]
        lines.append(f"• *{m.get('key')}* ({m.get('type', 'general')}): {val}")
    return "\n".join(lines)


def _slack_help() -> str:
    """Return help text for /dobby."""
    return (
        "*Dobby Slash Commands:*\n"
        "• `/dobby status` — Task queue overview\n"
        "• `/dobby summary` — Recent activity summary\n"
        "• `/dobby projects` — List registered projects\n"
        "• `/dobby add <project> <description>` — Queue a task\n"
        "• `/dobby recall <query>` — Search memories\n"
        "• `/dobby help` — This message"
    )
