import {
  type TaskItem,
  type TaskStatus,
  type TaskType,
  type TaskPriority,
  formatTaskList,
  formatTaskProgress,
  loadTasks,
  mutateTasks,
} from '@wrongstack/core';
import {
  addPlanItem,
  emptyPlan,
  formatPlan,
  loadPlan,
  savePlan,
} from '@wrongstack/core';
import type { SlashCommand } from '@wrongstack/core';
import type { SlashCommandContext } from './index.js';
import { parseSubcommand, unknownSubcommand } from './helpers.js';

function findTask(tasks: TaskItem[], query: string): { idx: number; item: TaskItem } | null {
  const asIndex = Number.parseInt(query, 10);
  if (!Number.isNaN(asIndex)) {
    const idx = asIndex - 1;
    const item = tasks[idx];
    if (item) return { idx, item };
  }
  const byId = tasks.findIndex((t) => t.id === query);
  if (byId >= 0) {
    const item = tasks[byId];
    if (item) return { idx: byId, item };
  }
  const q = query.toLowerCase();
  const byTitle = tasks.findIndex((t) => t.title.toLowerCase().includes(q));
  if (byTitle >= 0) {
    const item = tasks[byTitle];
    if (item) return { idx: byTitle, item };
  }
  return null;
}

function validateType(s: string): TaskType | null {
  const valid: TaskType[] = ['feature', 'bugfix', 'refactor', 'docs', 'test', 'chore'];
  return valid.includes(s as TaskType) ? (s as TaskType) : null;
}

function validatePriority(s: string): TaskPriority | null {
  const valid: TaskPriority[] = ['critical', 'high', 'medium', 'low'];
  return valid.includes(s as TaskPriority) ? (s as TaskPriority) : null;
}

function validateStatus(s: string): TaskStatus | null {
  const valid: TaskStatus[] = ['pending', 'in_progress', 'blocked', 'failed', 'review', 'completed'];
  return valid.includes(s as TaskStatus) ? (s as TaskStatus) : null;
}

export function buildTasksCommand(_opts: SlashCommandContext): SlashCommand {
  return {
    name: 'tasks',
    category: 'Inspect',
    description:
      'Manage structured tasks with dependencies, types, and priorities: /tasks [show|add <title>|start|done|fail|status <id> <status>|promote <id>|planify <id>|clear]',
    help: [
      'Usage:',
      '  /tasks                            Show task progress + list',
      '  /tasks show                       Same as no args',
      '  /tasks add <title> [type] [prio]  Add a task',
      '  /tasks start <id|index>           Mark task in-progress',
      '  /tasks done <id|index>            Mark task completed',
      '  /tasks fail <id|index>            Mark task failed',
      '  /tasks status <id> <status>       Set exact status (pending|in_progress|blocked|review|completed|failed)',
      '  /tasks depends <id> <depId...>    Set dependencies for a task',
      '  /tasks assign <id> <agent>        Assign task to an agent/subagent',
      '  /tasks promote <id>               Promote task to todo items',
      '  /tasks planify <id>               Promote task to a plan item',
      '  /tasks clear                      Remove all tasks',
      '',
      'Types: feature, bugfix, refactor, docs, test, chore',
      'Priorities: critical, high, medium, low',
    ].join('\n'),
    async run(args, ctx) {
      if (!ctx) {
        return { message: 'No agent context available.' };
      }
      const taskPath = (ctx.meta as Record<string, unknown>)?.['task.path'];
      if (typeof taskPath !== 'string' || !taskPath) {
        return { message: 'Task storage is not configured for this session.' };
      }
      const sessionId = ctx.session?.id ?? 'unknown';
      const { cmd, rest } = parseSubcommand(args);
      const restJoined = rest.join(' ').trim();

      // Read-only ops — no lock overhead
      if (cmd === '' || cmd === 'show' || cmd === 'list') {
        const file = await loadTasks(taskPath);
        return { message: formatTaskList(file?.tasks ?? []) };
      }
      if (cmd === 'progress' || cmd === 'statusline') {
        const file = await loadTasks(taskPath);
        return { message: formatTaskProgress(file?.tasks ?? []) };
      }

      // planify: reads tasks, writes plan — handled inside the task lock
      // to prevent TOCTOU between loadTasks and savePlan.
      if (cmd === 'planify') {
        if (!restJoined) return { message: 'Usage: /tasks planify <id|index>' };
        const planPath = (ctx.meta as Record<string, unknown>)?.['plan.path'];
        if (typeof planPath !== 'string' || !planPath) {
          return { message: 'Plan storage is not configured for this session.' };
        }

        let outputMessage = '';
        await mutateTasks(taskPath, sessionId, async (file) => {
          const found = findTask(file.tasks, restJoined);
          if (!found) {
            outputMessage = `No task matched "${restJoined}".`;
            return file;
          }
          const planCfg = (await loadPlan(planPath)) ?? emptyPlan(sessionId);
          const { plan: updated } = addPlanItem(planCfg, found.item.title, found.item.description);
          await savePlan(planPath, updated);
          outputMessage = `Planified "${found.item.title}" → plan item.\n${formatPlan(updated)}`;
          return file;
        });
        return { message: outputMessage };
      }

      // Mutating ops — locked via mutateTasks
      let outputMessage = '';
      await mutateTasks(taskPath, sessionId, async (file) => {
        switch (cmd) {
          case 'add': {
            if (!restJoined) { outputMessage = 'Usage: /tasks add <title> [type] [priority]'; return file; }
            const parts = restJoined.split(/\s+/);
            const title = parts[0] ?? '';
            const type = validateType(parts[1] ?? '') ?? 'feature';
            const priority = validatePriority(parts[2] ?? '') ?? 'medium';
            const now = new Date().toISOString();
            file.tasks.push({
              id: `task_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
              title, type, priority,
              status: 'pending',
              createdAt: now,
              updatedAt: now,
            });
            outputMessage = `Added: ${title}\n\n${formatTaskProgress(file.tasks)}`;
            break;
          }
          case 'start':
          case 'done':
          case 'fail': {
            if (!restJoined) { outputMessage = `Usage: /tasks ${cmd} <id|index>`; return file; }
            const found = findTask(file.tasks, restJoined);
            if (!found) { outputMessage = `No task matched "${restJoined}".`; return file; }
            const statusMap: Record<string, TaskStatus> = { start: 'in_progress', done: 'completed', fail: 'failed' };
            const verbMap: Record<string, string> = { start: 'Started', done: 'Completed', fail: 'Failed' };
            found.item.status = statusMap[cmd] ?? 'pending';
            found.item.updatedAt = new Date().toISOString();
            outputMessage = `Marked ${verbMap[cmd] ?? cmd}: ${found.item.title}\n\n${formatTaskProgress(file.tasks)}`;
            break;
          }
          case 'status': {
            if (rest.length < 2) { outputMessage = 'Usage: /tasks status <id> <status>'; return file; }
            const targetId = rest[0] ?? '';
            const newStatus = validateStatus(rest[1] ?? '');
            if (!newStatus) { outputMessage = `Invalid status "${rest[1]}".`; return file; }
            const found = findTask(file.tasks, targetId);
            if (!found) { outputMessage = `No task matched "${targetId}".`; return file; }
            found.item.status = newStatus;
            found.item.updatedAt = new Date().toISOString();
            outputMessage = `Status → ${newStatus}: ${found.item.title}\n\n${formatTaskProgress(file.tasks)}`;
            break;
          }
          case 'depends':
          case 'deps': {
            if (rest.length < 2) { outputMessage = 'Usage: /tasks depends <id> <depId1> [depId2 ...]'; return file; }
            const targetId = rest[0] ?? '';
            const depIds = rest.slice(1);
            const found = findTask(file.tasks, targetId);
            if (!found) { outputMessage = `No task matched "${targetId}".`; return file; }
            found.item.dependsOn = depIds;
            found.item.updatedAt = new Date().toISOString();
            outputMessage = `Dependencies set for "${found.item.title}": ${depIds.join(', ')}`;
            break;
          }
          case 'assign': {
            if (rest.length < 2) { outputMessage = 'Usage: /tasks assign <id> <agent>'; return file; }
            const targetId = rest[0] ?? '';
            const agent = rest.slice(1).join(' ');
            const found = findTask(file.tasks, targetId);
            if (!found) { outputMessage = `No task matched "${targetId}".`; return file; }
            found.item.assignee = agent;
            found.item.updatedAt = new Date().toISOString();
            outputMessage = `Assigned to ${agent}: "${found.item.title}"`;
            break;
          }
          case 'promote': {
            if (!restJoined) { outputMessage = 'Usage: /tasks promote <id|index>'; return file; }
            const found = findTask(file.tasks, restJoined);
            if (!found) { outputMessage = `No task matched "${restJoined}".`; return file; }
            found.item.status = 'in_progress';
            found.item.updatedAt = new Date().toISOString();
            const todos: Array<{ id: string; content: string; status: 'pending' | 'in_progress' | 'completed'; activeForm?: string; promotedFromTask?: string }> = [
              { id: `todo_${Date.now()}_task`, content: found.item.title, status: 'in_progress', activeForm: found.item.title, promotedFromTask: found.item.id },
            ];
            if (found.item.description) {
              todos.push({ id: `todo_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`, content: found.item.description.slice(0, 200), status: 'pending', promotedFromTask: found.item.id });
            }
            ctx.state.replaceTodos(todos);
            outputMessage = `Promoted to ${todos.length} todo(s): "${found.item.title}"\n\n${formatTaskProgress(file.tasks)}`;
            break;
          }
          case 'clear': {
            const n = file.tasks.length;
            if (n === 0) { outputMessage = 'Tasks were already empty.'; return file; }
            file.tasks = [];
            outputMessage = `Cleared ${n} task${n === 1 ? '' : 's'}.`;
            break;
          }
          default:
            outputMessage = unknownSubcommand(cmd, ['show', 'add', 'start', 'done', 'fail', 'status', 'depends', 'assign', 'promote', 'planify', 'clear'], 'tasks');
            return file;
        }
        return file;
      });

      return { message: outputMessage };
    },
  };
}
