import { JSONSchema7 } from 'json-schema';
import { randomUUID } from 'crypto';
import { createTool, createSuccessResult, createErrorResult } from '../../core/tool-framework.js';
import { ToolRegistration, RequestContext } from '../../core/types.js';

/**
 * Kanban Board Tools - 12-Factor MCP Implementation
 * 
 * Implements Factor 2: Deterministic Execution with structured outputs
 * Implements Factor 3: Stateless Processes with RequestContext
 * Implements Factor 4: Structured Outputs for LLM consumption
 */

// Input type interfaces
interface CreateBoardInput {
  name: string;
  description?: string;
  columns?: string[];
  workflowType?: 'simple' | 'development' | 'support' | 'custom';
}

interface CreateTaskInput {
  boardId: string;
  title: string;
  description?: string;
  assignedTo?: string;
  priority?: 'low' | 'medium' | 'high' | 'critical';
  tags?: string[];
  dueDate?: string;
  storyPoints?: number;
}

interface MoveTaskInput {
  taskId: string;
  toColumn: string;
  position?: number;
}

interface UpdateTaskInput {
  taskId: string;
  title?: string;
  description?: string;
  assignedTo?: string;
  priority?: 'low' | 'medium' | 'high' | 'critical';
  tags?: string[];
  dueDate?: string;
  storyPoints?: number;
}

interface GetBoardInput {
  boardId: string;
  includeArchived?: boolean;
}

interface ListTasksInput {
  boardId: string;
  column?: string;
  assignedTo?: string;
  priority?: 'low' | 'medium' | 'high' | 'critical';
  status?: 'active' | 'archived';
  limit?: number;
  offset?: number;
}

interface GetBoardAnalyticsInput {
  boardId: string;
  timeframe?: '24h' | '7d' | '30d' | '90d';
}

// Predefined workflow templates
const WORKFLOW_TEMPLATES = {
  simple: ['To Do', 'Doing', 'Done'],
  development: ['Backlog', 'To Do', 'In Progress', 'Code Review', 'Testing', 'Done'],
  support: ['New', 'Triaged', 'In Progress', 'Waiting for Customer', 'Resolved', 'Closed'],
  custom: []
};

/**
 * Create a new kanban board
 */
const createBoardTool = createTool<CreateBoardInput, any>({
  name: 'create_kanban_board',
  description: 'Create a new kanban board with customizable columns and workflow',
  category: 'kanban',
  inputSchema: {
    type: 'object',
    properties: {
      name: {
        type: 'string',
        description: 'Board name',
        minLength: 1,
        maxLength: 200
      },
      description: {
        type: 'string',
        description: 'Board description',
        maxLength: 1000
      },
      columns: {
        type: 'array',
        items: { type: 'string', minLength: 1, maxLength: 50 },
        description: 'Custom column names (overrides workflowType)',
        maxItems: 10
      },
      workflowType: {
        type: 'string',
        enum: ['simple', 'development', 'support', 'custom'],
        default: 'simple',
        description: 'Predefined workflow template'
      }
    },
    required: ['name'],
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: CreateBoardInput, context: RequestContext) {
    try {
      const boardId = randomUUID();
      const now = Date.now();

      // Check for duplicate board names
      const existingBoardCheck = await context.db.get(
        'SELECT id FROM kanban_boards WHERE name = ? AND project_id = ?',
        [input.name, context.projectId || 'default']
      );

      if (existingBoardCheck.success && existingBoardCheck.data) {
        return createErrorResult({
          code: 'DUPLICATE_RESOURCE',
          message: 'A board with this name already exists',
          category: 'validation'
        });
      }

      // Determine columns to use
      const columns = input.columns && input.columns.length > 0 
        ? input.columns 
        : WORKFLOW_TEMPLATES[input.workflowType || 'simple'] || WORKFLOW_TEMPLATES.simple;

      if (columns.length === 0) {
        return createErrorResult({
          code: 'INVALID_INPUT',
          message: 'At least one column is required',
          category: 'validation'
        });
      }

      // Insert board into database
      const result = await context.db.run(
        `INSERT INTO kanban_boards 
         (id, name, description, columns, workflow_type, project_id, created_at, updated_at) 
         VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
        [
          boardId,
          input.name,
          input.description || '',
          JSON.stringify(columns),
          input.workflowType || 'simple',
          context.projectId || 'default',
          now,
          now
        ]
      );

      if (!result.success) {
        return createErrorResult({
          code: 'DATABASE_ERROR',
          message: 'Failed to create board',
          details: { error: result.error },
          category: 'system'
        });
      }

      return createSuccessResult({
        board: {
          id: boardId,
          name: input.name,
          description: input.description || '',
          columns,
          workflowType: input.workflowType || 'simple',
          taskCount: 0,
          createdAt: new Date(now).toISOString()
        },
        message: `Kanban board "${input.name}" created successfully with ${columns.length} columns`,
        nextSteps: [
          'Add tasks to your board',
          'Customize column names if needed',
          'Set up team member assignments'
        ]
      });

    } catch (error) {
      return createErrorResult({
        code: 'EXECUTION_ERROR',
        message: `Failed to create board: ${error instanceof Error ? error.message : 'Unknown error'}`,
        category: 'execution'
      });
    }
  }
});

/**
 * Create a new task on a kanban board
 */
const createTaskTool = createTool<CreateTaskInput, any>({
  name: 'create_kanban_task',
  description: 'Create a new task on a kanban board',
  category: 'kanban',
  inputSchema: {
    type: 'object',
    properties: {
      boardId: {
        type: 'string',
        description: 'Board ID to create task on',
        pattern: '^[a-zA-Z0-9-]+$'
      },
      title: {
        type: 'string',
        description: 'Task title',
        minLength: 1,
        maxLength: 200
      },
      description: {
        type: 'string',
        description: 'Task description',
        maxLength: 2000
      },
      assignedTo: {
        type: 'string',
        description: 'Team member assigned to task',
        maxLength: 100
      },
      priority: {
        type: 'string',
        enum: ['low', 'medium', 'high', 'critical'],
        default: 'medium',
        description: 'Task priority level'
      },
      tags: {
        type: 'array',
        items: { type: 'string', maxLength: 50 },
        description: 'Task tags for categorization',
        maxItems: 10
      },
      dueDate: {
        type: 'string',
        format: 'date',
        description: 'Task due date (YYYY-MM-DD format)'
      },
      storyPoints: {
        type: 'integer',
        description: 'Story points for estimation',
        minimum: 1,
        maximum: 100
      }
    },
    required: ['boardId', 'title'],
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: CreateTaskInput, context: RequestContext) {
    try {
      // Verify board exists and get first column
      const boardResult = await context.db.get(
        'SELECT id, name, columns FROM kanban_boards WHERE id = ? AND project_id = ?',
        [input.boardId, context.projectId || 'default']
      );

      if (!boardResult.success || !boardResult.data) {
        return createErrorResult({
          code: 'RESOURCE_NOT_FOUND',
          message: 'Board not found',
          details: { boardId: input.boardId },
          category: 'validation'
        });
      }

      const board = boardResult.data;
      const columns = JSON.parse(board.columns);
      const firstColumn = columns[0];

      if (!firstColumn) {
        return createErrorResult({
          code: 'INVALID_STATE',
          message: 'Board has no columns',
          details: { boardId: input.boardId },
          category: 'validation'
        });
      }

      const taskId = randomUUID();
      const now = Date.now();

      // Get next position in first column
      const positionResult = await context.db.get(
        'SELECT MAX(position) as maxPosition FROM kanban_tasks WHERE board_id = ? AND column_name = ?',
        [input.boardId, firstColumn]
      );

      const nextPosition = (positionResult.data?.maxPosition || 0) + 1;

      // Validate due date if provided
      let dueDateTimestamp = null;
      if (input.dueDate) {
        const dueDate = new Date(input.dueDate);
        if (isNaN(dueDate.getTime())) {
          return createErrorResult({
            code: 'INVALID_INPUT',
            message: 'Invalid due date format',
            category: 'validation'
          });
        }
        dueDateTimestamp = dueDate.getTime();
      }

      // Insert task into database
      const result = await context.db.run(
        `INSERT INTO kanban_tasks 
         (id, board_id, title, description, assigned_to, priority, tags, 
          due_date, story_points, column_name, position, status, 
          project_id, created_at, updated_at) 
         VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
        [
          taskId,
          input.boardId,
          input.title,
          input.description || '',
          input.assignedTo || null,
          input.priority || 'medium',
          JSON.stringify(input.tags || []),
          dueDateTimestamp,
          input.storyPoints || null,
          firstColumn,
          nextPosition,
          'active',
          context.projectId || 'default',
          now,
          now
        ]
      );

      if (!result.success) {
        return createErrorResult({
          code: 'DATABASE_ERROR',
          message: 'Failed to create task',
          details: { error: result.error },
          category: 'system'
        });
      }

      return createSuccessResult({
        task: {
          id: taskId,
          boardId: input.boardId,
          title: input.title,
          description: input.description || '',
          assignedTo: input.assignedTo || null,
          priority: input.priority || 'medium',
          tags: input.tags || [],
          dueDate: input.dueDate || null,
          storyPoints: input.storyPoints || null,
          column: firstColumn,
          position: nextPosition,
          status: 'active',
          createdAt: new Date(now).toISOString()
        },
        message: `Task "${input.title}" created on board "${board.name}" in column "${firstColumn}"`,
        boardName: board.name,
        column: firstColumn
      });

    } catch (error) {
      return createErrorResult({
        code: 'EXECUTION_ERROR',
        message: `Failed to create task: ${error instanceof Error ? error.message : 'Unknown error'}`,
        category: 'execution'
      });
    }
  }
});

/**
 * Move a task between columns
 */
const moveTaskTool = createTool<MoveTaskInput, any>({
  name: 'move_kanban_task',
  description: 'Move a task to a different column on the kanban board',
  category: 'kanban',
  inputSchema: {
    type: 'object',
    properties: {
      taskId: {
        type: 'string',
        description: 'Task ID to move',
        pattern: '^[a-zA-Z0-9-]+$'
      },
      toColumn: {
        type: 'string',
        description: 'Target column name',
        minLength: 1,
        maxLength: 50
      },
      position: {
        type: 'integer',
        description: 'Position in target column (0 = top)',
        minimum: 0
      }
    },
    required: ['taskId', 'toColumn'],
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: MoveTaskInput, context: RequestContext) {
    try {
      // Get task and verify it exists
      const taskResult = await context.db.get(
        `SELECT t.*, b.name as board_name, b.columns 
         FROM kanban_tasks t 
         JOIN kanban_boards b ON t.board_id = b.id 
         WHERE t.id = ? AND t.project_id = ?`,
        [input.taskId, context.projectId || 'default']
      );

      if (!taskResult.success || !taskResult.data) {
        return createErrorResult({
          code: 'RESOURCE_NOT_FOUND',
          message: 'Task not found',
          details: { taskId: input.taskId },
          category: 'validation'
        });
      }

      const task = taskResult.data;
      const columns = JSON.parse(task.columns);

      // Verify target column exists
      if (!columns.includes(input.toColumn)) {
        return createErrorResult({
          code: 'INVALID_INPUT',
          message: `Column "${input.toColumn}" does not exist on this board`,
          details: { availableColumns: columns },
          category: 'validation'
        });
      }

      const oldColumn = task.column_name;

      // If moving to same column, just update position
      if (oldColumn === input.toColumn && input.position !== undefined) {
        // Update positions
        await context.db.transaction(async (tx) => {
          // Get current max position in target column
          const maxPosResult = await tx.get(
            'SELECT MAX(position) as maxPosition FROM kanban_tasks WHERE board_id = ? AND column_name = ?',
            [task.board_id, input.toColumn]
          );
          
          const maxPosition = maxPosResult?.maxPosition || 0;
          const targetPosition = Math.min(input.position, maxPosition);

          // Shift other tasks if needed
          if (targetPosition < task.position) {
            // Moving up - shift others down
            await tx.run(
              'UPDATE kanban_tasks SET position = position + 1 WHERE board_id = ? AND column_name = ? AND position >= ? AND position < ?',
              [task.board_id, input.toColumn, targetPosition, task.position]
            );
          } else if (targetPosition > task.position) {
            // Moving down - shift others up
            await tx.run(
              'UPDATE kanban_tasks SET position = position - 1 WHERE board_id = ? AND column_name = ? AND position > ? AND position <= ?',
              [task.board_id, input.toColumn, task.position, targetPosition]
            );
          }

          // Update task position
          await tx.run(
            'UPDATE kanban_tasks SET position = ?, updated_at = ? WHERE id = ?',
            [targetPosition, Date.now(), input.taskId]
          );
        });
      } else {
        // Moving between columns
        await context.db.transaction(async (tx) => {
          // Get next position in target column
          const maxPosResult = await tx.get(
            'SELECT MAX(position) as maxPosition FROM kanban_tasks WHERE board_id = ? AND column_name = ?',
            [task.board_id, input.toColumn]
          );
          
          const targetPosition = input.position !== undefined 
            ? Math.min(input.position, (maxPosResult?.maxPosition || 0) + 1)
            : (maxPosResult?.maxPosition || 0) + 1;

          // Shift tasks in target column if needed
          if (input.position !== undefined) {
            await tx.run(
              'UPDATE kanban_tasks SET position = position + 1 WHERE board_id = ? AND column_name = ? AND position >= ?',
              [task.board_id, input.toColumn, targetPosition]
            );
          }

          // Shift tasks in old column up
          await tx.run(
            'UPDATE kanban_tasks SET position = position - 1 WHERE board_id = ? AND column_name = ? AND position > ?',
            [task.board_id, oldColumn, task.position]
          );

          // Update task column and position
          await tx.run(
            'UPDATE kanban_tasks SET column_name = ?, position = ?, updated_at = ? WHERE id = ?',
            [input.toColumn, targetPosition, Date.now(), input.taskId]
          );
        });
      }

      return createSuccessResult({
        message: `Task "${task.title}" moved from "${oldColumn}" to "${input.toColumn}"`,
        taskId: input.taskId,
        taskTitle: task.title,
        fromColumn: oldColumn,
        toColumn: input.toColumn,
        boardName: task.board_name,
        newPosition: input.position
      });

    } catch (error) {
      return createErrorResult({
        code: 'EXECUTION_ERROR',
        message: `Failed to move task: ${error instanceof Error ? error.message : 'Unknown error'}`,
        category: 'execution'
      });
    }
  }
});

/**
 * Get board with all tasks
 */
const getBoardTool = createTool<GetBoardInput, any>({
  name: 'get_kanban_board',
  description: 'Get a kanban board with all its tasks organized by columns',
  category: 'kanban',
  readOnly: true,
  inputSchema: {
    type: 'object',
    properties: {
      boardId: {
        type: 'string',
        description: 'Board ID to retrieve',
        pattern: '^[a-zA-Z0-9-]+$'
      },
      includeArchived: {
        type: 'boolean',
        description: 'Include archived tasks',
        default: false
      }
    },
    required: ['boardId'],
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: GetBoardInput, context: RequestContext) {
    try {
      // Get board info
      const boardResult = await context.db.get(
        'SELECT * FROM kanban_boards WHERE id = ? AND project_id = ?',
        [input.boardId, context.projectId || 'default']
      );

      if (!boardResult.success || !boardResult.data) {
        return createErrorResult({
          code: 'RESOURCE_NOT_FOUND',
          message: 'Board not found',
          details: { boardId: input.boardId },
          category: 'validation'
        });
      }

      const board = boardResult.data;
      const columns = JSON.parse(board.columns);

      // Get all tasks for the board
      const tasksQuery = input.includeArchived 
        ? 'SELECT * FROM kanban_tasks WHERE board_id = ? ORDER BY column_name, position'
        : 'SELECT * FROM kanban_tasks WHERE board_id = ? AND status = ? ORDER BY column_name, position';
      
      const tasksParams = input.includeArchived 
        ? [input.boardId]
        : [input.boardId, 'active'];

      const tasksResult = await context.db.query(tasksQuery, tasksParams);

      if (!tasksResult.success) {
        return createErrorResult({
          code: 'DATABASE_ERROR',
          message: 'Failed to get board tasks',
          details: { error: tasksResult.error },
          category: 'system'
        });
      }

      // Organize tasks by column
      const tasksByColumn: Record<string, any[]> = {};
      columns.forEach((column: string) => {
        tasksByColumn[column] = [];
      });

      (tasksResult.data || []).forEach((task: any) => {
        if (tasksByColumn[task.column_name]) {
          tasksByColumn[task.column_name].push({
            id: task.id,
            title: task.title,
            description: task.description,
            assignedTo: task.assigned_to,
            priority: task.priority,
            tags: JSON.parse(task.tags || '[]'),
            dueDate: task.due_date ? new Date(task.due_date).toISOString() : null,
            storyPoints: task.story_points,
            position: task.position,
            status: task.status,
            createdAt: new Date(task.created_at).toISOString(),
            updatedAt: new Date(task.updated_at).toISOString()
          });
        }
      });

      // Calculate statistics
      const totalTasks = (tasksResult.data || []).length;
      const tasksByStatus = (tasksResult.data || []).reduce((acc: any, task: any) => {
        acc[task.status] = (acc[task.status] || 0) + 1;
        return acc;
      }, {});

      return createSuccessResult({
        board: {
          id: board.id,
          name: board.name,
          description: board.description,
          columns,
          workflowType: board.workflow_type,
          createdAt: new Date(board.created_at).toISOString(),
          updatedAt: new Date(board.updated_at).toISOString()
        },
        tasksByColumn,
        statistics: {
          totalTasks,
          tasksByStatus,
          tasksByColumn: Object.fromEntries(
            columns.map((col: string) => [col, tasksByColumn[col].length])
          )
        }
      });

    } catch (error) {
      return createErrorResult({
        code: 'EXECUTION_ERROR',
        message: `Failed to get board: ${error instanceof Error ? error.message : 'Unknown error'}`,
        category: 'execution'
      });
    }
  }
});

/**
 * Get board analytics and insights
 */
const getBoardAnalyticsTool = createTool<GetBoardAnalyticsInput, any>({
  name: 'get_kanban_analytics',
  description: 'Get analytics and insights for a kanban board including cycle time and throughput',
  category: 'kanban',
  readOnly: true,
  inputSchema: {
    type: 'object',
    properties: {
      boardId: {
        type: 'string',
        description: 'Board ID to analyze',
        pattern: '^[a-zA-Z0-9-]+$'
      },
      timeframe: {
        type: 'string',
        enum: ['24h', '7d', '30d', '90d'],
        default: '30d',
        description: 'Analysis timeframe'
      }
    },
    required: ['boardId'],
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: GetBoardAnalyticsInput, context: RequestContext) {
    try {
      // Verify board exists
      const boardResult = await context.db.get(
        'SELECT name, columns FROM kanban_boards WHERE id = ? AND project_id = ?',
        [input.boardId, context.projectId || 'default']
      );

      if (!boardResult.success || !boardResult.data) {
        return createErrorResult({
          code: 'RESOURCE_NOT_FOUND',
          message: 'Board not found',
          details: { boardId: input.boardId },
          category: 'validation'
        });
      }

      const board = boardResult.data;
      const columns = JSON.parse(board.columns);

      // Calculate time threshold
      const now = Date.now();
      let timeThreshold = now;
      switch (input.timeframe) {
        case '24h':
          timeThreshold = now - (24 * 60 * 60 * 1000);
          break;
        case '7d':
          timeThreshold = now - (7 * 24 * 60 * 60 * 1000);
          break;
        case '30d':
          timeThreshold = now - (30 * 24 * 60 * 60 * 1000);
          break;
        case '90d':
          timeThreshold = now - (90 * 24 * 60 * 60 * 1000);
          break;
      }

      // Get task statistics for the timeframe
      const tasksResult = await context.db.query(
        `SELECT 
           COUNT(*) as total_tasks,
           COUNT(CASE WHEN status = 'active' THEN 1 END) as active_tasks,
           COUNT(CASE WHEN status = 'archived' THEN 1 END) as completed_tasks,
           AVG(CASE WHEN status = 'archived' THEN updated_at - created_at END) as avg_cycle_time,
           column_name,
           priority,
           assigned_to
         FROM kanban_tasks 
         WHERE board_id = ? AND created_at > ?
         GROUP BY column_name, priority, assigned_to`,
        [input.boardId, timeThreshold]
      );

      // Get task distribution by column
      const columnStatsResult = await context.db.query(
        'SELECT column_name, COUNT(*) as count FROM kanban_tasks WHERE board_id = ? AND status = ? GROUP BY column_name',
        [input.boardId, 'active']
      );

      const columnStats = (columnStatsResult.data || []).reduce((acc: any, row: any) => {
        acc[row.column_name] = row.count;
        return acc;
      }, {});

      // Ensure all columns are represented
      columns.forEach((col: string) => {
        if (!(col in columnStats)) {
          columnStats[col] = 0;
        }
      });

      // Calculate throughput (completed tasks)
      const throughputResult = await context.db.get(
        'SELECT COUNT(*) as completed FROM kanban_tasks WHERE board_id = ? AND status = ? AND updated_at > ?',
        [input.boardId, 'archived', timeThreshold]
      );

      const throughput = throughputResult.data?.completed || 0;

      // Get priority distribution
      const priorityResult = await context.db.query(
        'SELECT priority, COUNT(*) as count FROM kanban_tasks WHERE board_id = ? AND status = ? GROUP BY priority',
        [input.boardId, 'active']
      );

      const priorityStats = (priorityResult.data || []).reduce((acc: any, row: any) => {
        acc[row.priority] = row.count;
        return acc;
      }, {});

      // Get assignee workload
      const assigneeResult = await context.db.query(
        'SELECT assigned_to, COUNT(*) as count FROM kanban_tasks WHERE board_id = ? AND status = ? AND assigned_to IS NOT NULL GROUP BY assigned_to',
        [input.boardId, 'active']
      );

      const assigneeWorkload = (assigneeResult.data || []).reduce((acc: any, row: any) => {
        acc[row.assigned_to] = row.count;
        return acc;
      }, {});

      const taskStats = tasksResult.data?.[0] || {};
      const avgCycleTimeMs = taskStats.avg_cycle_time || 0;
      const avgCycleTimeDays = avgCycleTimeMs > 0 ? Math.round((avgCycleTimeMs / (1000 * 60 * 60 * 24)) * 100) / 100 : 0;

      return createSuccessResult({
        board: {
          id: input.boardId,
          name: board.name
        },
        timeframe: input.timeframe,
        metrics: {
          totalTasks: taskStats.total_tasks || 0,
          activeTasks: taskStats.active_tasks || 0,
          completedTasks: taskStats.completed_tasks || 0,
          throughput,
          averageCycleTime: {
            milliseconds: avgCycleTimeMs,
            days: avgCycleTimeDays,
            humanReadable: avgCycleTimeDays > 0 ? `${avgCycleTimeDays} days` : 'No data'
          }
        },
        distribution: {
          byColumn: columnStats,
          byPriority: priorityStats,
          byAssignee: assigneeWorkload
        },
        insights: [
          throughput > 0 ? `Completed ${throughput} tasks in the last ${input.timeframe}` : 'No tasks completed in timeframe',
          avgCycleTimeDays > 0 ? `Average cycle time: ${avgCycleTimeDays} days` : 'No cycle time data available',
          Object.keys(assigneeWorkload).length > 0 ? `${Object.keys(assigneeWorkload).length} team members have active tasks` : 'No assigned tasks'
        ]
      });

    } catch (error) {
      return createErrorResult({
        code: 'EXECUTION_ERROR',
        message: `Failed to get board analytics: ${error instanceof Error ? error.message : 'Unknown error'}`,
        category: 'execution'
      });
    }
  }
});

/**
 * Setup kanban tools
 */
export async function setupKanbanTools(): Promise<ToolRegistration> {
  return {
    module: 'kanban',
    tools: [
      createBoardTool,
      createTaskTool,
      moveTaskTool,
      getBoardTool,
      getBoardAnalyticsTool
    ]
  };
}