/**
 * 任务管理工具（1 个）
 *
 * task_manager — 创建/更新/列出/完成任务
 *
 * 支持 Agent 在执行复杂任务时将任务分解为子任务并跟踪状态。
 * 任务持久化到 SQLite tasks 表。
 *
 * @see docs/生产级通用 AI Agent 智能体桌面应用：完整设计与构建指南.html
 */

import { nanoid } from 'nanoid';
import type Database from 'better-sqlite3';
import type { IMetonaTool, ToolExecutionContext } from '../../types/metona-tool';
import type { MetonaToolDef } from '../../../harness/types';
import { MetonaToolCategory, MetonaRiskLevel } from '../../../harness/types';

export type TaskStatus = 'pending' | 'in_progress' | 'completed' | 'blocked' | 'cancelled';
export type TaskPriority = 'low' | 'medium' | 'high' | 'critical';

export interface Task {
  id: string;
  sessionId: string;
  title: string;
  description: string;
  status: TaskStatus;
  priority: TaskPriority;
  parentId: string | null;
  assignedTo: string | null;
  order: number;
  createdAt: number;
  updatedAt: number;
  completedAt: number | null;
}

/** 从数据库行映射到 Task 对象 */
function mapRow(row: Record<string, unknown>): Task {
  return {
    id: row.id as string,
    sessionId: row.session_id as string,
    title: row.title as string,
    description: row.description as string,
    status: row.status as TaskStatus,
    priority: row.priority as TaskPriority,
    parentId: (row.parent_id as string) ?? null,
    assignedTo: (row.assigned_to as string) ?? null,
    order: row.order_idx as number,
    createdAt: row.created_at as number,
    updatedAt: row.updated_at as number,
    completedAt: (row.completed_at as number) ?? null,
  };
}

// T3.5: 合法枚举值
const VALID_STATUSES: TaskStatus[] = ['pending', 'in_progress', 'completed', 'blocked', 'cancelled'];
const VALID_PRIORITIES: TaskPriority[] = ['low', 'medium', 'high', 'critical'];

export class TaskManagerTool implements IMetonaTool {
  readonly definition: MetonaToolDef = {
    name: 'task_manager',
    description: 'Manage tasks for complex multi-step work. Supports operations: create, update, list, complete, delete. Tasks are persisted per session and can have parent-child relationships.',
    parameters: {
      type: 'object',
      properties: {
        operation: {
          type: 'string',
          description: 'Task operation',
          enum: ['create', 'update', 'list', 'complete', 'delete', 'get'],
        },
        title: { type: 'string', description: 'Task title (for create)' },
        description: { type: 'string', description: 'Task description (for create/update)' },
        task_id: { type: 'string', description: 'Task ID (for update/complete/delete/get)' },
        status: { type: 'string', description: 'New status (for update)', enum: ['pending', 'in_progress', 'completed', 'blocked', 'cancelled'] },
        priority: { type: 'string', description: 'Priority (for create/update)', enum: ['low', 'medium', 'high', 'critical'] },
        parent_id: { type: 'string', description: 'Parent task ID (for create, establishes subtask relationship)' },
      },
      required: ['operation'],
    },
    category: MetonaToolCategory.DATABASE,
    riskLevel: MetonaRiskLevel.LOW,
    requiresPermission: false,
    timeoutMs: 10_000,
  };

  constructor(
    private getDB: () => Database.Database,
    // P2（v0.3.13）: 写入后回调，用于通知 UI 刷新（避免工具层依赖 Electron）
    private onTaskChanged?: (sessionId: string) => void,
  ) {}

  /** 写入后触发回调（仅对修改操作） */
  private notifyChanged(sessionId: string): void {
    if (this.onTaskChanged) {
      try {
        this.onTaskChanged(sessionId);
      } catch {
        // 回调失败不影响工具主流程
      }
    }
  }

  async execute(args: Record<string, unknown>, context: ToolExecutionContext): Promise<unknown> {
    try {
      const operation = args.operation as string;

      switch (operation) {
        case 'create':
          return this.createTask(args, context);
        case 'update':
          return this.updateTask(args, context);
        case 'list':
          return this.listTasks(args, context);
        case 'complete':
          return this.completeTask(args, context);
        case 'delete':
          return this.deleteTask(args, context);
        case 'get':
          return this.getTask(args, context);
        default:
          return { success: false, error: `Unknown operation: ${operation}` };
      }
    } catch (err) {
      return { success: false, error: (err as Error).message };
    }
  }

  private createTask(args: Record<string, unknown>, context: ToolExecutionContext): unknown {
    const db = this.getDB();
    const id = `task_${nanoid(12)}`;
    const now = Date.now();
    const sessionId = context.sessionId;
    const title = args.title as string;
    const description = (args.description as string) ?? '';
    const priority = (args.priority as TaskPriority) ?? 'medium';
    const parentId = (args.parent_id as string) ?? null;

    if (!title) {
      return { success: false, error: 'title is required for create operation' };
    }

    // T3.5: 校验 priority 枚举值
    if (!VALID_PRIORITIES.includes(priority)) {
      return { success: false, error: `Invalid priority: ${priority}. Must be one of: ${VALID_PRIORITIES.join(', ')}` };
    }

    // 获取同 session 同 parent 下的最大 order
    const maxOrderRow = db.prepare(
      'SELECT MAX(order_idx) as max_order FROM tasks WHERE session_id = ? AND parent_id IS ?',
    ).get(sessionId, parentId) as { max_order: number | null } | undefined;
    const order = (maxOrderRow?.max_order ?? -1) + 1;

    db.prepare(`
      INSERT INTO tasks (id, session_id, title, description, status, priority, parent_id, assigned_to, order_idx, created_at, updated_at)
      VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
    `).run(id, sessionId, title, description, 'pending', priority, parentId, null, order, now, now);

    // P2: 通知 UI 刷新
    this.notifyChanged(sessionId);

    return {
      success: true,
      task: {
        id, sessionId, title, description,
        status: 'pending', priority, parentId,
        assignedTo: null, order,
        createdAt: now, updatedAt: now, completedAt: null,
      },
    };
  }

  private updateTask(args: Record<string, unknown>, context: ToolExecutionContext): unknown {
    const db = this.getDB();
    const taskId = args.task_id as string;
    if (!taskId) {
      return { success: false, error: 'task_id is required for update operation' };
    }

    // T3.1: 校验任务属于当前会话
    const existing = db.prepare('SELECT * FROM tasks WHERE id = ? AND session_id = ?').get(taskId, context.sessionId);
    if (!existing) {
      return { success: false, error: `Task not found: ${taskId}` };
    }

    // T3.5: 校验枚举值
    if (args.status !== undefined && !VALID_STATUSES.includes(args.status as TaskStatus)) {
      return { success: false, error: `Invalid status: ${args.status}. Must be one of: ${VALID_STATUSES.join(', ')}` };
    }
    if (args.priority !== undefined && !VALID_PRIORITIES.includes(args.priority as TaskPriority)) {
      return { success: false, error: `Invalid priority: ${args.priority}. Must be one of: ${VALID_PRIORITIES.join(', ')}` };
    }

    const updates: string[] = [];
    const values: unknown[] = [];

    if (args.title !== undefined) {
      updates.push('title = ?');
      values.push(args.title);
    }
    if (args.description !== undefined) {
      updates.push('description = ?');
      values.push(args.description);
    }
    if (args.status !== undefined) {
      updates.push('status = ?');
      values.push(args.status);
      if (args.status === 'completed') {
        updates.push('completed_at = ?');
        values.push(Date.now());
      }
    }
    if (args.priority !== undefined) {
      updates.push('priority = ?');
      values.push(args.priority);
    }

    if (updates.length === 0) {
      return { success: false, error: 'No fields to update' };
    }

    updates.push('updated_at = ?');
    values.push(Date.now());
    values.push(taskId);
    values.push(context.sessionId);

    // T3.1: UPDATE 语句加 AND session_id = ? 防止越权
    db.prepare(`UPDATE tasks SET ${updates.join(', ')} WHERE id = ? AND session_id = ?`).run(...values);

    // P2: 通知 UI 刷新
    this.notifyChanged(context.sessionId);

    const updated = db.prepare('SELECT * FROM tasks WHERE id = ? AND session_id = ?').get(taskId, context.sessionId) as Record<string, unknown>;
    return { success: true, task: mapRow(updated) };
  }

  private listTasks(args: Record<string, unknown>, context: ToolExecutionContext): unknown {
    const db = this.getDB();
    const sessionId = context.sessionId;
    const status = args.status as string | undefined;

    let sql = 'SELECT * FROM tasks WHERE session_id = ?';
    const values: unknown[] = [sessionId];

    if (status) {
      sql += ' AND status = ?';
      values.push(status);
    }

    sql += ' ORDER BY order_idx ASC, created_at ASC';

    const rows = db.prepare(sql).all(...values) as Array<Record<string, unknown>>;
    const tasks = rows.map(mapRow);

    return {
      success: true,
      tasks,
      count: tasks.length,
      by_status: {
        pending: tasks.filter((t) => t.status === 'pending').length,
        in_progress: tasks.filter((t) => t.status === 'in_progress').length,
        completed: tasks.filter((t) => t.status === 'completed').length,
        blocked: tasks.filter((t) => t.status === 'blocked').length,
        cancelled: tasks.filter((t) => t.status === 'cancelled').length,
      },
    };
  }

  private completeTask(args: Record<string, unknown>, context: ToolExecutionContext): unknown {
    const db = this.getDB();
    const taskId = args.task_id as string;
    if (!taskId) {
      return { success: false, error: 'task_id is required for complete operation' };
    }

    // T3.1: 校验任务属于当前会话
    const existing = db.prepare('SELECT id FROM tasks WHERE id = ? AND session_id = ?').get(taskId, context.sessionId);
    if (!existing) {
      return { success: false, error: `Task not found: ${taskId}` };
    }

    const now = Date.now();
    // T3.1: UPDATE 语句加 AND session_id = ? 防止越权
    const result = db.prepare(
      'UPDATE tasks SET status = ?, completed_at = ?, updated_at = ? WHERE id = ? AND session_id = ?',
    ).run('completed', now, now, taskId, context.sessionId);

    if (result.changes === 0) {
      return { success: false, error: `Task not found: ${taskId}` };
    }

    // P2: 通知 UI 刷新
    this.notifyChanged(context.sessionId);

    return { success: true, task_id: taskId, completed_at: now };
  }

  private deleteTask(args: Record<string, unknown>, context: ToolExecutionContext): unknown {
    const db = this.getDB();
    const taskId = args.task_id as string;
    if (!taskId) {
      return { success: false, error: 'task_id is required for delete operation' };
    }

    // T3.1: 校验任务属于当前会话
    const existing = db.prepare('SELECT id FROM tasks WHERE id = ? AND session_id = ?').get(taskId, context.sessionId);
    if (!existing) {
      return { success: false, error: `Task not found: ${taskId}` };
    }

    // T3.2/T3.3: 递归 CTE 查出所有后代任务 ID
    const descendantIds = db.prepare(`
      WITH RECURSIVE descendants AS (
        SELECT id FROM tasks WHERE id = ?
        UNION ALL
        SELECT t.id FROM tasks t JOIN descendants d ON t.parent_id = d.id
      )
      SELECT id FROM descendants
    `).all(taskId) as Array<{ id: string }>;

    const allIds = descendantIds.map((r) => r.id);

    // 事务包裹批量删除
    const deleteMany = db.transaction(() => {
      const placeholders = allIds.map(() => '?').join(',');
      db.prepare(`DELETE FROM tasks WHERE id IN (${placeholders})`).run(...allIds);
    });
    deleteMany();

    // P2: 通知 UI 刷新
    this.notifyChanged(context.sessionId);

    return { success: true, task_id: taskId, deleted: allIds.length };
  }

  private getTask(args: Record<string, unknown>, context: ToolExecutionContext): unknown {
    const db = this.getDB();
    const taskId = args.task_id as string;
    if (!taskId) {
      return { success: false, error: 'task_id is required for get operation' };
    }

    // T3.1: 校验任务属于当前会话
    const row = db.prepare('SELECT * FROM tasks WHERE id = ? AND session_id = ?').get(taskId, context.sessionId) as Record<string, unknown> | undefined;
    if (!row) {
      return { success: false, error: `Task not found: ${taskId}` };
    }

    // 获取子任务（同样限制在当前会话内）
    const subtasks = db.prepare('SELECT * FROM tasks WHERE parent_id = ? AND session_id = ? ORDER BY order_idx ASC').all(taskId, context.sessionId) as Array<Record<string, unknown>>;

    return {
      success: true,
      task: mapRow(row),
      subtasks: subtasks.map(mapRow),
    };
  }
}
