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';
import { CommonSchemas } from '../../core/validation.js';

/**
 * Agile Management 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
 */

// Status transition validation
type StoryStatus = 'todo' | 'in_progress' | 'review' | 'done' | 'blocked' | 'cancelled';

interface StatusTransitionResult {
  isValid: boolean;
  error?: string;
  suggestedAction?: string;
}

/**
 * Validates story status transitions based on workflow rules
 * Key rule: Stories must be in 'todo' status before moving to 'in_progress'
 */
function validateStatusTransition(
  fromStatus: StoryStatus, 
  toStatus: StoryStatus, 
  skipValidation = false
): StatusTransitionResult {
  // Allow bypass for admin overrides
  if (skipValidation) {
    return { isValid: true };
  }


  // Allow transitions within the same status
  if (fromStatus === toStatus) {
    return { isValid: true };
  }

  // Allow transitions to in_progress from todo and blocked
  if (toStatus === 'in_progress') {
    if (fromStatus === 'todo' || fromStatus === 'blocked') {
      return { isValid: true };
    }
    
    // Block other transitions to in_progress
    return {
      isValid: false,
      error: `Cannot transition from "${fromStatus}" to "in_progress"`,
      suggestedAction: 'Stories must be in "todo" status before moving to "in_progress"'
    };
  }

  // Define other allowed transitions
  const allowedTransitions: Record<StoryStatus, StoryStatus[]> = {
    todo: ['in_progress', 'blocked', 'cancelled'],
    in_progress: ['review', 'blocked', 'cancelled'],
    review: ['done', 'in_progress', 'blocked', 'cancelled'],
    done: ['review'], // Can reopen if needed
    blocked: ['todo', 'in_progress', 'review', 'cancelled'],
    cancelled: ['todo'] // Can reactivate cancelled stories
  };

  const allowed = allowedTransitions[fromStatus] || [];
  if (!allowed.includes(toStatus)) {
    return {
      isValid: false,
      error: `Cannot transition from "${fromStatus}" to "${toStatus}"`,
      suggestedAction: `Allowed transitions from "${fromStatus}": ${allowed.join(', ')}`
    };
  }

  return { isValid: true };
}

// Input type interfaces
interface CreateSprintInput {
  name: string;
  goal: string;
  duration?: number;
  startDate?: string;
  team?: string[];
}

interface AddStoryToSprintInput {
  storyId: string;
  sprintId: string;
}

interface CreateStoryInput {
  title: string;
  description?: string;
  acceptanceCriteria?: string[];
  storyPoints?: number;
  priority?: 'low' | 'medium' | 'high' | 'critical';
  tags?: string[];
  assignedTo?: string;
  epicId?: string;
  sprintId?: string;
  status?: 'todo' | 'in_progress' | 'review' | 'done';
}

interface UpdateStoryStatusInput {
  storyId: string;
  status: 'todo' | 'in_progress' | 'review' | 'done';
  notes?: string;
  skipWorkflowValidation?: boolean;
}

interface GetSprintStatsInput {
  sprintId: string;
}

interface ListStoriesInput {
  sprintId?: string;
  status?: 'todo' | 'in_progress' | 'review' | 'done';
  assignedTo?: string;
  limit?: number;
  offset?: number;
}

interface CreateEpicInput {
  title: string;
  description: string;
  goals?: string[];
  priority?: 'low' | 'medium' | 'high' | 'critical';
  owner?: string;
  targetDate?: string;
  successCriteria?: string[];
  repositories?: string[];
}

interface GetEpicInput {
  epicId: string;
}

interface ListEpicsInput {
  status?: 'active' | 'completed' | 'on_hold';
  owner?: string;
  limit?: number;
  offset?: number;
}

interface ListSprintsInput {
  status?: 'active' | 'completed';
  includeMetrics?: boolean;
  limit?: number;
  offset?: number;
}

interface GetSprintInput {
  sprintId: string;
}

interface ListBacklogInput {
  epicId?: string;
  priority?: 'low' | 'medium' | 'high' | 'critical';
  tags?: string[];
  limit?: number;
  offset?: number;
}

interface UpdateEpicInput {
  epicId: string;
  title?: string;
  description?: string;
  goals?: string[];
  priority?: 'low' | 'medium' | 'high' | 'critical';
  owner?: string;
  targetDate?: string;
  successCriteria?: string[];
  repositories?: string[];
  status?: 'active' | 'completed' | 'on_hold';
}

interface UpdateSprintInput {
  sprintId: string;
  name?: string;
  goal?: string;
  status?: 'active' | 'completed';
  endDate?: string;
  team?: string[];
}

interface UpdateStoryInput {
  storyId: string;
  title?: string;
  description?: string;
  acceptanceCriteria?: string[];
  storyPoints?: number;
  priority?: 'low' | 'medium' | 'high' | 'critical';
  tags?: string[];
  assignedTo?: string;
  epicId?: string;
  status?: 'todo' | 'in_progress' | 'review' | 'done';
}

interface GetStoryInput {
  storyId: string;
}

interface AssignStoryToSprintInput {
  storyId: string;
  sprintId: string;
}

interface RecordStandupInput {
  sprintId: string;
  teamMember: string;
  yesterday: string;
  today: string;
  blockers: string[];
  mood?: 'great' | 'good' | 'okay' | 'struggling';
}

interface GenerateBurndownInput {
  sprintId: string;
}

interface GenerateVelocityInput {
  teamId?: string;
  numberOfSprints?: number;
}

// Phase 1 New Interfaces
interface CompleteSprintInput {
  sprintId: string;
  carryoverStories?: string[];
  completionNotes?: string;
  completedBy?: string;
}

interface BlockStoryInput {
  storyId: string;
  blockerType: 'dependency' | 'external' | 'technical' | 'resource';
  reason: string;
  blockerStoryId?: string;
}

interface UnblockStoryInput {
  blockerId: string;
  resolution: string;
  resolvedBy?: string;
}

interface AssignStoryInput {
  storyId: string;
  assignee: string;
  notes?: string;
}

interface AddStoryCommentInput {
  storyId: string;
  content: string;
  author: string;
  threadId?: string;
  parentCommentId?: string;
}

interface SplitStoryInput {
  storyId: string;
  newStories: Array<{
    title: string;
    description?: string;
    acceptanceCriteria?: string[];
    storyPoints?: number;
    priority?: 'low' | 'medium' | 'high' | 'critical';
  }>;
  notes?: string;
}

interface LinkStoryToEpicInput {
  storyId: string;
  epicId: string;
  notes?: string;
}

// Phase 2 Epic Management Interfaces
interface GetEpicProgressInput {
  epicId: string;
}

interface GetEpicTimelineInput {
  epicId: string;
  includeDependencies?: boolean;
}

interface CloseEpicInput {
  epicId: string;
  completionNotes?: string;
  businessValueRealized?: string;
  closedBy?: string;
}

interface GetEpicStoriesInput {
  epicId: string;
  status?: 'todo' | 'in_progress' | 'review' | 'done' | 'blocked';
  sprintId?: string;
  assignee?: string;
}

interface ValidateEpicRequirementsInput {
  epicId: string;
}

interface GenerateEpicReportInput {
  epicId: string;
  includeMetrics?: boolean;
  includeTimeline?: boolean;
}

// Phase 2 Advanced Reporting Interfaces
interface GenerateSprintReportInput {
  sprintId: string;
  includeVelocity?: boolean;
  includeBlockers?: boolean;
}

interface GetTeamVelocityTrendInput {
  teamId?: string;
  numberOfSprints?: number;
  startDate?: string;
  endDate?: string;
}

interface GetCycleTimeMetricsInput {
  sprintId?: string;
  storyIds?: string[];
  dateRange?: {
    start: string;
    end: string;
  };
}

interface GenerateRetrospectiveTemplateInput {
  sprintId: string;
  includeMetrics?: boolean;
  templateType?: 'standard' | 'starfish' | 'sailboat';
}

interface GetCrossSprintAnalyticsInput {
  numberOfSprints?: number;
  teamId?: string;
  epicId?: string;
}

interface GenerateEpicBurndownInput {
  epicId: string;
  granularity?: 'daily' | 'weekly' | 'sprint';
}

interface GetDependencyReportInput {
  sprintId?: string;
  epicId?: string;
  includeResolved?: boolean;
}

/**
 * Create a new sprint
 */
const createSprintTool = createTool<CreateSprintInput, any>({
  name: 'create_agile_sprint',
  description: 'Create a new sprint with goals, timeline, and team assignment',
  category: 'agile-management',
  inputSchema: {
    type: 'object',
    properties: {
      name: {
        type: 'string',
        description: 'Sprint name',
        minLength: 1,
        maxLength: 200
      },
      goal: {
        type: 'string',
        description: 'Sprint goal and objectives',
        minLength: 1,
        maxLength: 1000
      },
      duration: {
        type: 'integer',
        description: 'Sprint duration in days',
        minimum: 1,
        maximum: 90,
        default: 14
      },
      startDate: {
        type: 'string',
        format: 'date',
        description: 'Sprint start date (YYYY-MM-DD format)'
      },
      team: {
        type: 'array',
        items: { type: 'string' },
        description: 'Team member names or IDs',
        maxItems: 20
      }
    },
    required: ['name', 'goal'],
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: CreateSprintInput, context: RequestContext) {
    try {
      const sprintId = randomUUID();
      const now = Date.now();
      const startDate = input.startDate ? new Date(input.startDate) : new Date();
      const endDate = new Date(startDate);
      endDate.setDate(endDate.getDate() + (input.duration || 14));

      // Validate start date is not in the past
      if (startDate < new Date(Date.now() - 24 * 60 * 60 * 1000)) {
        return createErrorResult({
          code: 'INVALID_INPUT',
          message: 'Start date cannot be in the past',
          category: 'validation'
        });
      }

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

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

      // Insert sprint into database
      const result = await context.db.run(
        `INSERT INTO agile_sprints 
         (id, name, goal, duration, start_date, end_date, status, 
          team, project_id, created_at, updated_at) 
         VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
        [
          sprintId,
          input.name,
          input.goal,
          input.duration || 14,
          startDate.getTime(),
          endDate.getTime(),
          'active',
          JSON.stringify(input.team || []),
          context.projectId || 'default',
          now,
          now
        ]
      );

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

      return createSuccessResult({
        sprint: {
          id: sprintId,
          name: input.name,
          goal: input.goal,
          duration: input.duration || 14,
          startDate: startDate.toISOString(),
          endDate: endDate.toISOString(),
          status: 'active',
          team: input.team || [],
          storyCount: 0,
          completedStories: 0
        },
        message: `Sprint "${input.name}" created successfully`,
        nextSteps: [
          'Add user stories to the sprint backlog',
          'Conduct sprint planning session',
          'Set story estimates and assignments'
        ]
      });

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

/**
 * Create a new user story
 */
const createStoryTool = createTool<CreateStoryInput, any>({
  name: 'create_user_story',
  description: 'Create a new user story with acceptance criteria and estimates',
  category: 'agile-management',
  inputSchema: {
    type: 'object',
    properties: {
      title: {
        type: 'string',
        description: 'Story title',
        minLength: 1,
        maxLength: 200
      },
      description: {
        type: 'string',
        description: 'Detailed story description',
        maxLength: 2000
      },
      acceptanceCriteria: {
        type: 'array',
        items: { type: 'string', maxLength: 500 },
        description: 'List of acceptance criteria',
        maxItems: 10
      },
      storyPoints: {
        type: 'integer',
        description: 'Story complexity in points',
        minimum: 1,
        maximum: 100
      },
      priority: {
        type: 'string',
        enum: ['low', 'medium', 'high', 'critical'],
        default: 'medium',
        description: 'Story priority level'
      },
      tags: {
        type: 'array',
        items: { type: 'string', maxLength: 50 },
        description: 'Story tags for categorization',
        maxItems: 10
      },
      assignedTo: {
        type: 'string',
        description: 'Team member assigned to the story',
        maxLength: 100
      },
      epicId: {
        type: 'string',
        description: 'Parent epic ID',
        pattern: '^[a-zA-Z0-9-]+$'
      },
      sprintId: {
        type: 'string',
        description: 'Sprint ID to assign the story to',
        pattern: '^[a-zA-Z0-9-]+$'
      },
      status: {
        type: 'string',
        description: 'Initial status for the story',
        enum: ['todo', 'in_progress', 'review', 'done'],
        default: 'todo'
      }
    },
    required: ['title'],
    additionalProperties: false
  } as JSONSchema7,

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

      // Validate epic exists if provided
      if (input.epicId) {
        const epicCheck = await context.db.get(
          'SELECT id FROM agile_epics WHERE id = ? AND project_id = ?',
          [input.epicId, context.projectId || 'default']
        );

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

      // Insert story into database
      const result = await context.db.run(
        `INSERT INTO agile_stories 
         (id, title, description, acceptance_criteria, story_points, priority, 
          tags, assignee, epic_id, sprint_id, status, project_id, created_at, updated_at) 
         VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
        [
          storyId,
          input.title,
          input.description || '',
          JSON.stringify(input.acceptanceCriteria || []),
          input.storyPoints || null,
          input.priority || 'medium',
          JSON.stringify(input.tags || []),
          input.assignedTo || null,
          input.epicId || null,
          input.sprintId || null,
          input.status || 'todo',
          context.projectId || 'default',
          now,
          now
        ]
      );

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

      return createSuccessResult({
        story: {
          id: storyId,
          title: input.title,
          description: input.description || '',
          acceptanceCriteria: input.acceptanceCriteria || [],
          storyPoints: input.storyPoints || null,
          priority: input.priority || 'medium',
          tags: input.tags || [],
          assignedTo: input.assignedTo || null,
          epicId: input.epicId || null,
          status: 'todo',
          createdAt: new Date(now).toISOString()
        },
        message: `Story "${input.title}" created successfully`,
        nextSteps: [
          'Add story to a sprint backlog',
          'Refine acceptance criteria if needed',
          'Estimate story points with the team'
        ]
      });

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

/**
 * Add a story to a sprint
 */
const addStoryToSprintTool = createTool<AddStoryToSprintInput, any>({
  name: 'add_story_to_sprint',
  description: 'Add an existing user story to a sprint backlog',
  category: 'agile-management',
  inputSchema: {
    type: 'object',
    properties: {
      storyId: {
        type: 'string',
        description: 'Story ID to add to sprint',
        pattern: '^[a-zA-Z0-9-]+$'
      },
      sprintId: {
        type: 'string',
        description: 'Sprint ID to add story to',
        pattern: '^[a-zA-Z0-9-]+$'
      }
    },
    required: ['storyId', 'sprintId'],
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: AddStoryToSprintInput, context: RequestContext) {
    try {
      // Verify story exists and is not already in a sprint
      const storyCheck = await context.db.get(
        'SELECT id, title, sprint_id FROM agile_stories WHERE id = ? AND project_id = ?',
        [input.storyId, context.projectId || 'default']
      );

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

      if (storyCheck.data.sprint_id) {
        return createErrorResult({
          code: 'INVALID_STATE',
          message: 'Story is already assigned to a sprint',
          details: { 
            storyId: input.storyId,
            currentSprintId: storyCheck.data.sprint_id 
          },
          category: 'validation'
        });
      }

      // Verify sprint exists
      const sprintCheck = await context.db.get(
        'SELECT id, name, status FROM agile_sprints WHERE id = ? AND project_id = ?',
        [input.sprintId, context.projectId || 'default']
      );

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

      // Add story to sprint
      const result = await context.db.run(
        'UPDATE agile_stories SET sprint_id = ?, updated_at = ? WHERE id = ?',
        [input.sprintId, Date.now(), input.storyId]
      );

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

      return createSuccessResult({
        message: `Story "${storyCheck.data.title}" added to sprint "${sprintCheck.data.name}"`,
        storyId: input.storyId,
        sprintId: input.sprintId,
        sprintName: sprintCheck.data.name,
        storyTitle: storyCheck.data.title
      });

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

/**
 * Update story status
 */
const updateStoryStatusTool = createTool<UpdateStoryStatusInput, any>({
  name: 'update_story_status',
  description: 'Update the status of a user story (todo, in_progress, review, done)',
  category: 'agile-management',
  inputSchema: {
    type: 'object',
    properties: {
      storyId: {
        type: 'string',
        description: 'Story ID to update',
        pattern: '^[a-zA-Z0-9-]+$'
      },
      status: {
        type: 'string',
        enum: ['todo', 'in_progress', 'review', 'done'],
        description: 'New status for the story'
      },
      notes: {
        type: 'string',
        description: 'Optional notes about the status change',
        maxLength: 1000
      },
      skipWorkflowValidation: {
        type: 'boolean',
        description: 'Skip workflow validation (admin override)',
        default: false
      }
    },
    required: ['storyId', 'status'],
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: UpdateStoryStatusInput, context: RequestContext) {
    try {
      // Verify story exists
      const storyCheck = await context.db.get(
        'SELECT id, title, status FROM agile_stories WHERE id = ? AND project_id = ?',
        [input.storyId, context.projectId || 'default']
      );

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

      const oldStatus = storyCheck.data.status;

      // Validate status transition
      const validation = validateStatusTransition(
        oldStatus as StoryStatus, 
        input.status as StoryStatus, 
        input.skipWorkflowValidation
      );

      if (!validation.isValid) {
        return createErrorResult({
          code: 'WORKFLOW_VALIDATION_ERROR',
          message: validation.error || 'Invalid status transition',
          details: { 
            currentStatus: oldStatus,
            attemptedStatus: input.status,
            suggestedAction: validation.suggestedAction
          },
          category: 'validation'
        });
      }

      // Update story status
      const result = await context.db.run(
        'UPDATE agile_stories SET status = ?, updated_at = ? WHERE id = ?',
        [input.status, Date.now(), input.storyId]
      );

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

      // Log status change (always log, with special notes for workflow bypass)
      const historyNotes = input.skipWorkflowValidation 
        ? `${input.notes || ''} [WORKFLOW VALIDATION BYPASSED]`.trim()
        : input.notes || `Status changed from ${oldStatus} to ${input.status}`;
      
      await context.db.run(
        `INSERT INTO story_history 
         (id, story_id, action, old_value, new_value, notes, user_id, created_at) 
         VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
        [
          randomUUID(),
          input.storyId,
          'status_change',
          oldStatus,
          input.status,
          historyNotes,
          context.userId || 'system',
          Date.now()
        ]
      );

      return createSuccessResult({
        message: `Story "${storyCheck.data.title}" status updated from ${oldStatus} to ${input.status}`,
        storyId: input.storyId,
        storyTitle: storyCheck.data.title,
        oldStatus,
        newStatus: input.status,
        notes: input.notes || null,
        workflowValidationBypassed: input.skipWorkflowValidation || false
      });

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

/**
 * Get sprint statistics
 */
const getSprintStatsTool = createTool<GetSprintStatsInput, any>({
  name: 'get_sprint_stats',
  description: 'Get detailed statistics and progress for a sprint',
  category: 'agile-management',
  readOnly: true,
  inputSchema: {
    type: 'object',
    properties: {
      sprintId: {
        type: 'string',
        description: 'Sprint ID to get statistics for',
        pattern: '^[a-zA-Z0-9-]+$'
      }
    },
    required: ['sprintId'],
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: GetSprintStatsInput, context: RequestContext) {
    try {
      // Get sprint basic info
      const sprintResult = await context.db.get(
        'SELECT * FROM agile_sprints WHERE id = ? AND project_id = ?',
        [input.sprintId, context.projectId || 'default']
      );

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

      const sprint = sprintResult.data;

      // Get story statistics
      const statsResult = await context.db.query(
        `SELECT 
           COUNT(*) as total_stories,
           COUNT(CASE WHEN status = 'done' THEN 1 END) as completed_stories,
           COUNT(CASE WHEN status = 'in_progress' THEN 1 END) as in_progress_stories,
           COUNT(CASE WHEN status = 'review' THEN 1 END) as review_stories,
           COUNT(CASE WHEN status = 'todo' THEN 1 END) as todo_stories,
           SUM(story_points) as total_points,
           SUM(CASE WHEN status = 'done' THEN story_points ELSE 0 END) as completed_points
         FROM agile_stories 
         WHERE sprint_id = ?`,
        [input.sprintId]
      );

      const stats = statsResult.data?.[0] || {};

      // Calculate progress metrics
      const totalStories = stats.total_stories || 0;
      const completedStories = stats.completed_stories || 0;
      const totalPoints = stats.total_points || 0;
      const completedPoints = stats.completed_points || 0;

      const storyProgress = totalStories > 0 ? (completedStories / totalStories) * 100 : 0;
      const pointsProgress = totalPoints > 0 ? (completedPoints / totalPoints) * 100 : 0;

      // Calculate days elapsed and remaining
      const now = Date.now();
      const startDate = new Date(sprint.start_date);
      const endDate = new Date(sprint.end_date);
      const totalDays = Math.ceil((endDate.getTime() - startDate.getTime()) / (1000 * 60 * 60 * 24));
      const daysElapsed = Math.max(0, Math.ceil((now - startDate.getTime()) / (1000 * 60 * 60 * 24)));
      const daysRemaining = Math.max(0, Math.ceil((endDate.getTime() - now) / (1000 * 60 * 60 * 24)));

      return createSuccessResult({
        sprint: {
          id: sprint.id,
          name: sprint.name,
          goal: sprint.goal,
          status: sprint.status,
          startDate: new Date(sprint.start_date).toISOString(),
          endDate: new Date(sprint.end_date).toISOString(),
          team: JSON.parse(sprint.team_members || '[]')
        },
        progress: {
          stories: {
            total: totalStories,
            completed: completedStories,
            inProgress: stats.in_progress_stories || 0,
            review: stats.review_stories || 0,
            todo: stats.todo_stories || 0,
            completionPercentage: Math.round(storyProgress * 100) / 100
          },
          storyPoints: {
            total: totalPoints,
            completed: completedPoints,
            remaining: totalPoints - completedPoints,
            completionPercentage: Math.round(pointsProgress * 100) / 100
          },
          timeline: {
            totalDays,
            daysElapsed,
            daysRemaining,
            timeElapsedPercentage: totalDays > 0 ? Math.round((daysElapsed / totalDays) * 100) : 0
          }
        },
        burndown: {
          idealBurnRate: totalPoints / totalDays,
          actualBurnRate: daysElapsed > 0 ? completedPoints / daysElapsed : 0,
          projectedCompletion: completedPoints > 0 && daysElapsed > 0 ? 
            Math.ceil(totalPoints / (completedPoints / daysElapsed)) : totalDays
        }
      });

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

/**
 * List stories with filtering
 */
const listStoriesTool = createTool<ListStoriesInput, any>({
  name: 'list_stories',
  description: 'List user stories with optional filtering by sprint, status, or assignee',
  category: 'agile-management',
  readOnly: true,
  inputSchema: {
    type: 'object',
    properties: {
      sprintId: {
        type: 'string',
        description: 'Filter by sprint ID',
        pattern: '^[a-zA-Z0-9-]+$'
      },
      status: {
        type: 'string',
        enum: ['todo', 'in_progress', 'review', 'done'],
        description: 'Filter by story status'
      },
      assignedTo: {
        type: 'string',
        description: 'Filter by assigned team member',
        maxLength: 100
      },
      limit: {
        type: 'integer',
        description: 'Maximum number of stories to return',
        minimum: 1,
        maximum: 100,
        default: 20
      },
      offset: {
        type: 'integer',
        description: 'Number of stories to skip',
        minimum: 0,
        default: 0
      }
    },
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: ListStoriesInput, context: RequestContext) {
    try {
      let sql = 'SELECT * FROM agile_stories WHERE project_id = ?';
      const params: any[] = [context.projectId || 'default'];

      if (input.sprintId) {
        sql += ' AND sprint_id = ?';
        params.push(input.sprintId);
      }

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

      if (input.assignedTo) {
        sql += ' AND assigned_to = ?';
        params.push(input.assignedTo);
      }

      sql += ' ORDER BY created_at DESC LIMIT ? OFFSET ?';
      params.push(input.limit || 20, input.offset || 0);

      const result = await context.db.query(sql, params);

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

      const stories = (result.data || []).map((story: any) => ({
        id: story.id,
        title: story.title,
        description: story.description,
        acceptanceCriteria: JSON.parse(story.acceptance_criteria || '[]'),
        storyPoints: story.story_points,
        priority: story.priority,
        status: story.status,
        tags: JSON.parse(story.tags || '[]'),
        assignedTo: story.assigned_to,
        sprintId: story.sprint_id,
        epicId: story.epic_id,
        createdAt: new Date(story.created_at).toISOString(),
        updatedAt: new Date(story.updated_at).toISOString()
      }));

      return createSuccessResult({
        stories,
        count: stories.length,
        hasMore: stories.length === (input.limit || 20),
        filters: {
          sprintId: input.sprintId || null,
          status: input.status || null,
          assignedTo: input.assignedTo || null
        }
      });

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

/**
 * List stories with comprehensive details
 */
const listStoriesWithDetailsTool = createTool({
  name: 'list_stories_with_details',
  description: 'List stories with comprehensive details including documentation fields, status, and metadata. Use this to audit documentation completeness, review story details in bulk, or generate reports.',
  
  inputSchema: {
    type: 'object' as const,
    properties: {
      sprintId: {
        type: 'string',
        description: 'Filter stories by sprint ID'
      },
      epicId: {
        type: 'string',
        description: 'Filter stories by epic ID'
      },
      status: {
        type: 'string',
        enum: ['backlog', 'todo', 'in_progress', 'review', 'testing', 'done', 'blocked', 'cancelled'],
        description: 'Filter by status'
      },
      includeFields: {
        type: 'array',
        items: {
          type: 'string',
          enum: [
            'id', 'title', 'description', 'status', 'storyPoints', 'priority',
            'assignee', 'epic', 'sprint', 'tags', 'acceptanceCriteria',
            'implementationDocumentUrl', 'designDocumentUrl', 'documentationStatus',
            'documentationLastReviewed', 'documentationReviewers', 'groomedWithUserFeedback',
            'blockedReason', 'startDate', 'dueDate', 'actualStartDate', 'actualEndDate',
            'estimatedHours', 'actualHours', 'completionPercentage', 'createdAt', 'updatedAt'
          ]
        },
        description: 'Specific fields to include in the response',
        default: ['id', 'title', 'status', 'storyPoints', 'implementationDocumentUrl', 'designDocumentUrl', 'documentationStatus']
      },
      limit: {
        type: 'integer',
        minimum: 1,
        maximum: 100,
        default: 50,
        description: 'Maximum number of stories to return'
      }
    },
    required: [],
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: any, context: RequestContext) {
    try {
      const {
        sprintId,
        epicId,
        status,
        includeFields = ['id', 'title', 'status', 'storyPoints', 'implementationDocumentUrl', 'designDocumentUrl', 'documentationStatus'],
        limit = 50
      } = input;

      // Build query
      let sql = 'SELECT * FROM agile_stories WHERE 1=1';
      const params: any[] = [];
      
      if (sprintId) {
        sql += ' AND sprint_id = ?';
        params.push(sprintId);
      }
      if (epicId) {
        sql += ' AND epic_id = ?';
        params.push(epicId);
      }
      if (status) {
        sql += ' AND status = ?';
        params.push(status);
      }
      
      sql += ` ORDER BY created_at DESC LIMIT ?`;
      params.push(limit);

      const result = await context.db.query(sql, params);

      if (!result.success) {
        throw new Error(result.error || 'Failed to query stories');
      }

      // Map to include only requested fields
      const stories = (result.data || []).map((story: any) => {
        const mappedStory: any = {};
        
        // Field mapping from database columns to API fields
        const fieldMapping: Record<string, string> = {
          'id': 'id',
          'title': 'title',
          'description': 'description',
          'status': 'status',
          'storyPoints': 'story_points',
          'priority': 'priority',
          'assignee': 'assigned_to',
          'epic': 'epic_id',
          'sprint': 'sprint_id',
          'tags': 'tags',
          'acceptanceCriteria': 'acceptance_criteria',
          'implementationDocumentUrl': 'implementation_doc_url',
          'designDocumentUrl': 'design_doc_url',
          'documentationStatus': 'documentation_status',
          'documentationLastReviewed': 'documentation_last_reviewed',
          'documentationReviewers': 'documentation_reviewers',
          'groomedWithUserFeedback': 'groomed_with_user_feedback',
          'blockedReason': 'blocked_reason',
          'startDate': 'start_date',
          'dueDate': 'due_date',
          'actualStartDate': 'actual_start_date',
          'actualEndDate': 'actual_end_date',
          'estimatedHours': 'estimated_hours',
          'actualHours': 'actual_hours',
          'completionPercentage': 'completion_percentage',
          'createdAt': 'created_at',
          'updatedAt': 'updated_at'
        };
        
        includeFields.forEach((field: string) => {
          const dbField = fieldMapping[field];
          if (dbField && story[dbField] !== undefined) {
            // Parse JSON fields
            if (['tags', 'acceptanceCriteria', 'documentationReviewers'].includes(field)) {
              try {
                mappedStory[field] = JSON.parse(story[dbField] || '[]');
              } catch {
                mappedStory[field] = [];
              }
            } else if (['createdAt', 'updatedAt'].includes(field) && story[dbField]) {
              mappedStory[field] = new Date(story[dbField]).toISOString();
            } else {
              mappedStory[field] = story[dbField];
            }
          }
        });
        
        return mappedStory;
      });
      
      // Generate summary statistics
      const totalStories = stories.length;
      const missingDocumentation = stories.filter((s: any) => 
        (includeFields.includes('implementationDocumentUrl') && !s.implementationDocumentUrl) ||
        (includeFields.includes('designDocumentUrl') && !s.designDocumentUrl)
      ).length;
      
      const statusCounts = stories.reduce((acc: any, story: any) => {
        if (story.status) {
          acc[story.status] = (acc[story.status] || 0) + 1;
        }
        return acc;
      }, {});

      return createSuccessResult({
        stories,
        summary: {
          total: totalStories,
          missingDocumentation,
          statusDistribution: statusCounts,
          filters: {
            sprintId,
            epicId,
            status,
            fieldsIncluded: includeFields
          }
        }
      });

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

/**
 * Create a new epic
 */
const createEpicTool = createTool<CreateEpicInput, any>({
  name: 'create_epic',
  description: 'Create a new epic to group related user stories',
  category: 'agile-management',
  inputSchema: {
    type: 'object',
    properties: {
      title: {
        type: 'string',
        description: 'Epic title',
        minLength: 1,
        maxLength: 200
      },
      description: {
        type: 'string',
        description: 'Detailed epic description',
        minLength: 1,
        maxLength: 5000
      },
      goals: {
        type: 'array',
        items: { type: 'string', maxLength: 500 },
        description: 'Epic goals and objectives',
        maxItems: 10
      },
      priority: {
        type: 'string',
        enum: ['low', 'medium', 'high', 'critical'],
        default: 'medium',
        description: 'Epic priority level'
      },
      owner: {
        type: 'string',
        description: 'Epic owner email or name',
        maxLength: 100
      },
      targetDate: {
        type: 'string',
        format: 'date',
        description: 'Target completion date'
      },
      successCriteria: {
        type: 'array',
        items: { type: 'string', maxLength: 500 },
        description: 'Success criteria for the epic',
        maxItems: 10
      },
      repositories: {
        type: 'array',
        items: { type: 'string', maxLength: 100 },
        description: 'Repositories involved in this epic',
        maxItems: 10
      }
    },
    required: ['title', 'description'],
    additionalProperties: false
  } as JSONSchema7,

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

      // Insert epic into database
      const result = await context.db.run(
        `INSERT INTO agile_epics 
         (id, title, description, goals, priority, owner, target_date, 
          success_criteria, repositories, status, project_id, created_at, updated_at) 
         VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
        [
          epicId,
          input.title,
          input.description,
          JSON.stringify(input.goals || []),
          input.priority || 'medium',
          input.owner || null,
          input.targetDate ? new Date(input.targetDate).getTime() : null,
          JSON.stringify(input.successCriteria || []),
          JSON.stringify(input.repositories || []),
          'planning',
          context.projectId || 'default',
          now,
          now
        ]
      );

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

      return createSuccessResult({
        epic: {
          id: epicId,
          title: input.title,
          description: input.description,
          goals: input.goals || [],
          priority: input.priority || 'medium',
          owner: input.owner || null,
          targetDate: input.targetDate || null,
          successCriteria: input.successCriteria || [],
          repositories: input.repositories || [],
          status: 'active',
          storyCount: 0,
          completedStories: 0
        },
        message: `Epic "${input.title}" created successfully`,
        nextSteps: [
          'Add user stories to this epic',
          'Define success criteria',
          'Assign to sprints'
        ]
      });

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

/**
 * Get epic details
 */
const getEpicTool = createTool<GetEpicInput, any>({
  name: 'get_epic',
  description: 'Get detailed information about a specific epic',
  category: 'agile-management',
  readOnly: true,
  inputSchema: {
    type: 'object',
    properties: {
      epicId: {
        type: 'string',
        description: 'Epic ID',
        pattern: '^[a-zA-Z0-9-]+$'
      }
    },
    required: ['epicId'],
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: GetEpicInput, context: RequestContext) {
    try {
      // Get epic details
      const epicResult = await context.db.get(
        'SELECT * FROM agile_epics WHERE id = ? AND project_id = ?',
        [input.epicId, context.projectId || 'default']
      );

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

      const epic = epicResult.data;

      // Get story count for this epic
      const storyCountResult = await context.db.query(
        `SELECT 
           COUNT(*) as total_stories,
           COUNT(CASE WHEN status = 'done' THEN 1 END) as completed_stories
         FROM agile_stories 
         WHERE epic_id = ?`,
        [input.epicId]
      );

      const stats = storyCountResult.data?.[0] || { total_stories: 0, completed_stories: 0 };

      return createSuccessResult({
        epic: {
          id: epic.id,
          title: epic.title,
          description: epic.description,
          goals: JSON.parse(epic.goals || '[]'),
          priority: epic.priority,
          owner: epic.owner,
          targetDate: epic.target_date ? new Date(epic.target_date).toISOString() : null,
          successCriteria: JSON.parse(epic.success_criteria || '[]'),
          repositories: JSON.parse(epic.repositories || '[]'),
          status: epic.status,
          storyCount: stats.total_stories || 0,
          completedStories: stats.completed_stories || 0,
          progress: stats.total_stories > 0 ? 
            Math.round((stats.completed_stories / stats.total_stories) * 100) : 0,
          createdAt: new Date(epic.created_at).toISOString(),
          updatedAt: new Date(epic.updated_at).toISOString()
        }
      });

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

/**
 * List epics with filtering
 */
const listEpicsTool = createTool<ListEpicsInput, any>({
  name: 'list_epics',
  description: 'List epics with optional filtering by status or owner',
  category: 'agile-management',
  readOnly: true,
  inputSchema: {
    type: 'object',
    properties: {
      status: {
        type: 'string',
        enum: ['active', 'completed', 'cancelled'],
        description: 'Filter by epic status'
      },
      owner: {
        type: 'string',
        description: 'Filter by epic owner',
        maxLength: 100
      },
      limit: {
        type: 'integer',
        description: 'Maximum number of epics to return',
        minimum: 1,
        maximum: 100,
        default: 20
      },
      offset: {
        type: 'integer',
        description: 'Number of epics to skip',
        minimum: 0,
        default: 0
      }
    },
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: ListEpicsInput, context: RequestContext) {
    try {
      let sql = 'SELECT * FROM agile_epics WHERE project_id = ?';
      const params: any[] = [context.projectId || 'default'];

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

      if (input.owner) {
        sql += ' AND owner = ?';
        params.push(input.owner);
      }

      sql += ' ORDER BY created_at DESC LIMIT ? OFFSET ?';
      params.push(input.limit || 20, input.offset || 0);

      const result = await context.db.query(sql, params);

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

      // Get story counts for each epic
      const epics = await Promise.all((result.data || []).map(async (epic: any) => {
        const storyCountResult = await context.db.query(
          `SELECT 
             COUNT(*) as total_stories,
             COUNT(CASE WHEN status = 'done' THEN 1 END) as completed_stories
           FROM agile_stories 
           WHERE epic_id = ?`,
          [epic.id]
        );

        const stats = storyCountResult.data?.[0] || { total_stories: 0, completed_stories: 0 };

        return {
          id: epic.id,
          title: epic.title,
          description: epic.description,
          priority: epic.priority,
          owner: epic.owner,
          status: epic.status,
          storyCount: stats.total_stories || 0,
          completedStories: stats.completed_stories || 0,
          progress: stats.total_stories > 0 ? 
            Math.round((stats.completed_stories / stats.total_stories) * 100) : 0,
          targetDate: epic.target_date ? new Date(epic.target_date).toISOString() : null,
          createdAt: new Date(epic.created_at).toISOString()
        };
      }));

      return createSuccessResult({
        epics,
        count: epics.length,
        hasMore: epics.length === (input.limit || 20),
        filters: {
          status: input.status || null,
          owner: input.owner || null
        }
      });

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

/**
 * List sprints with filtering
 */
const listSprintsTool = createTool<ListSprintsInput, any>({
  name: 'list_agile_sprints',
  description: 'List all sprints with optional filtering and metrics',
  category: 'agile-management',
  readOnly: true,
  inputSchema: {
    type: 'object',
    properties: {
      status: {
        type: 'string',
        enum: ['active', 'completed', 'cancelled'],
        description: 'Filter by sprint status'
      },
      includeMetrics: {
        type: 'boolean',
        description: 'Include sprint metrics',
        default: false
      },
      limit: {
        type: 'integer',
        description: 'Maximum number of sprints to return',
        minimum: 1,
        maximum: 100,
        default: 20
      },
      offset: {
        type: 'integer',
        description: 'Number of sprints to skip',
        minimum: 0,
        default: 0
      }
    },
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: ListSprintsInput, context: RequestContext) {
    try {
      let sql = 'SELECT * FROM agile_sprints WHERE project_id = ?';
      const params: any[] = [context.projectId || 'default'];

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

      sql += ' ORDER BY start_date DESC LIMIT ? OFFSET ?';
      params.push(input.limit || 20, input.offset || 0);

      const result = await context.db.query(sql, params);

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

      let sprints = (result.data || []).map((sprint: any) => ({
        id: sprint.id,
        name: sprint.name,
        goal: sprint.goal,
        status: sprint.status,
        duration: sprint.duration_days,
        startDate: new Date(sprint.start_date).toISOString(),
        endDate: new Date(sprint.end_date).toISOString(),
        team: JSON.parse(sprint.team_members || '[]'),
        createdAt: new Date(sprint.created_at).toISOString()
      }));

      // Add metrics if requested
      if (input.includeMetrics) {
        sprints = await Promise.all(sprints.map(async (sprint: any) => {
          const metricsResult = await context.db.query(
            `SELECT 
               COUNT(*) as total_stories,
               COUNT(CASE WHEN status = 'done' THEN 1 END) as completed_stories,
               SUM(story_points) as total_points,
               SUM(CASE WHEN status = 'done' THEN story_points ELSE 0 END) as completed_points
             FROM agile_stories 
             WHERE sprint_id = ?`,
            [sprint.id]
          );

          const metrics = metricsResult.data?.[0] || {};

          return {
            ...sprint,
            metrics: {
              totalStories: metrics.total_stories || 0,
              completedStories: metrics.completed_stories || 0,
              totalPoints: metrics.total_points || 0,
              completedPoints: metrics.completed_points || 0,
              completionPercentage: metrics.total_stories > 0 ?
                Math.round((metrics.completed_stories / metrics.total_stories) * 100) : 0
            }
          };
        }));
      }

      return createSuccessResult({
        sprints,
        count: sprints.length,
        hasMore: sprints.length === (input.limit || 20),
        filters: {
          status: input.status || null,
          includeMetrics: input.includeMetrics || false
        }
      });

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

/**
 * Get sprint details
 */
const getSprintTool = createTool<GetSprintInput, any>({
  name: 'get_sprint',
  description: 'Get detailed information about a specific sprint',
  category: 'agile-management',
  readOnly: true,
  inputSchema: {
    type: 'object',
    properties: {
      sprintId: {
        type: 'string',
        description: 'Sprint ID',
        pattern: '^[a-zA-Z0-9-]+$'
      }
    },
    required: ['sprintId'],
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: GetSprintInput, context: RequestContext) {
    try {
      // Get sprint details
      const sprintResult = await context.db.get(
        'SELECT * FROM agile_sprints WHERE id = ? AND project_id = ?',
        [input.sprintId, context.projectId || 'default']
      );

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

      const sprint = sprintResult.data;

      // Get stories in this sprint
      const storiesResult = await context.db.query(
        'SELECT * FROM agile_stories WHERE sprint_id = ? ORDER BY priority DESC, created_at ASC',
        [input.sprintId]
      );

      const stories = (storiesResult.data || []).map((story: any) => ({
        id: story.id,
        title: story.title,
        status: story.status,
        priority: story.priority,
        storyPoints: story.story_points,
        assignedTo: story.assigned_to,
        epicId: story.epic_id
      }));

      // Get sprint metrics
      const metricsResult = await context.db.query(
        `SELECT 
           COUNT(*) as total_stories,
           COUNT(CASE WHEN status = 'done' THEN 1 END) as completed_stories,
           COUNT(CASE WHEN status = 'in_progress' THEN 1 END) as in_progress_stories,
           COUNT(CASE WHEN status = 'review' THEN 1 END) as review_stories,
           COUNT(CASE WHEN status = 'todo' THEN 1 END) as todo_stories,
           SUM(story_points) as total_points,
           SUM(CASE WHEN status = 'done' THEN story_points ELSE 0 END) as completed_points
         FROM agile_stories 
         WHERE sprint_id = ?`,
        [input.sprintId]
      );

      const metrics = metricsResult.data?.[0] || {};

      return createSuccessResult({
        sprint: {
          id: sprint.id,
          name: sprint.name,
          goal: sprint.goal,
          status: sprint.status,
          duration: sprint.duration,
          startDate: new Date(sprint.start_date).toISOString(),
          endDate: new Date(sprint.end_date).toISOString(),
          team: JSON.parse(sprint.team || '[]'),
          createdAt: new Date(sprint.created_at).toISOString(),
          updatedAt: new Date(sprint.updated_at).toISOString()
        },
        stories,
        progress: {
          stories: {
            total: metrics.total_stories || 0,
            completed: metrics.completed_stories || 0,
            inProgress: metrics.in_progress_stories || 0,
            review: metrics.review_stories || 0,
            todo: metrics.todo_stories || 0
          },
          points: {
            total: metrics.total_points || 0,
            completed: metrics.completed_points || 0
          },
          completionPercentage: metrics.total_stories > 0 ?
            Math.round((metrics.completed_stories / metrics.total_stories) * 100) : 0,
          velocity: sprint.velocity || 0
        }
      });

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

/**
 * List backlog stories
 */
const listBacklogTool = createTool<ListBacklogInput, any>({
  name: 'list_agile_backlog',
  description: 'List all stories not assigned to any sprint (product backlog)',
  category: 'agile-management',
  readOnly: true,
  inputSchema: {
    type: 'object',
    properties: {
      epicId: {
        type: 'string',
        description: 'Filter by epic ID',
        pattern: '^[a-zA-Z0-9-]+$'
      },
      priority: {
        type: 'string',
        enum: ['low', 'medium', 'high', 'critical'],
        description: 'Filter by priority'
      },
      tags: {
        type: 'array',
        items: { type: 'string' },
        description: 'Filter by tags',
        maxItems: 10
      },
      limit: {
        type: 'integer',
        description: 'Maximum number of stories to return',
        minimum: 1,
        maximum: 100,
        default: 50
      },
      offset: {
        type: 'integer',
        description: 'Number of stories to skip',
        minimum: 0,
        default: 0
      }
    },
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: ListBacklogInput, context: RequestContext) {
    try {
      let sql = 'SELECT * FROM agile_stories WHERE project_id = ? AND sprint_id IS NULL';
      const params: any[] = [context.projectId || 'default'];

      if (input.epicId) {
        sql += ' AND epic_id = ?';
        params.push(input.epicId);
      }

      if (input.priority) {
        sql += ' AND priority = ?';
        params.push(input.priority);
      }

      if (input.tags && input.tags.length > 0) {
        // Filter by tags using JSON contains
        const tagConditions = input.tags.map(() => 'tags LIKE ?').join(' OR ');
        sql += ` AND (${tagConditions})`;
        input.tags.forEach(tag => params.push(`%"${tag}"%`));
      }

      sql += ' ORDER BY priority DESC, created_at ASC LIMIT ? OFFSET ?';
      params.push(input.limit || 50, input.offset || 0);

      const result = await context.db.query(sql, params);

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

      const stories = (result.data || []).map((story: any) => ({
        id: story.id,
        title: story.title,
        description: story.description,
        acceptanceCriteria: JSON.parse(story.acceptance_criteria || '[]'),
        storyPoints: story.story_points,
        priority: story.priority,
        status: story.status,
        tags: JSON.parse(story.tags || '[]'),
        assignedTo: story.assigned_to,
        epicId: story.epic_id,
        createdAt: new Date(story.created_at).toISOString(),
        updatedAt: new Date(story.updated_at).toISOString()
      }));

      // Group by priority for better visualization
      const groupedByPriority = {
        critical: stories.filter((s: any) => s.priority === 'critical'),
        high: stories.filter((s: any) => s.priority === 'high'),
        medium: stories.filter((s: any) => s.priority === 'medium'),
        low: stories.filter((s: any) => s.priority === 'low')
      };

      return createSuccessResult({
        stories,
        count: stories.length,
        hasMore: stories.length === (input.limit || 50),
        groupedByPriority,
        totalPoints: stories.reduce((sum: number, story: any) => sum + (story.storyPoints || 0), 0),
        filters: {
          epicId: input.epicId || null,
          priority: input.priority || null,
          tags: input.tags || []
        }
      });

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

/**
 * Update an epic
 */
const updateEpicTool = createTool<UpdateEpicInput, any>({
  name: 'update_epic',
  description: 'Update an existing epic',
  category: 'agile-management',
  inputSchema: {
    type: 'object',
    properties: {
      epicId: {
        type: 'string',
        description: 'Epic ID to update',
        pattern: '^[a-zA-Z0-9-]+$'
      },
      title: {
        type: 'string',
        description: 'New title for the epic',
        minLength: 1,
        maxLength: 200
      },
      description: {
        type: 'string',
        description: 'New description',
        maxLength: 2000
      },
      goals: {
        type: 'array',
        description: 'Updated business goals',
        items: { type: 'string' }
      },
      priority: {
        type: 'string',
        enum: ['low', 'medium', 'high', 'critical'],
        description: 'New priority level'
      },
      owner: {
        type: 'string',
        description: 'New owner',
        maxLength: 100
      },
      targetDate: {
        type: 'string',
        format: 'date',
        description: 'New target completion date'
      },
      successCriteria: {
        type: 'array',
        description: 'Updated success criteria',
        items: { type: 'string' }
      },
      repositories: {
        type: 'array',
        description: 'Updated repository links',
        items: { type: 'string' }
      },
      status: {
        type: 'string',
        enum: ['active', 'completed', 'cancelled'],
        description: 'New status'
      }
    },
    required: ['epicId'],
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: UpdateEpicInput, context: RequestContext) {
    try {
      // Check if epic exists
      const epicResult = await context.db.get(
        'SELECT * FROM agile_epics WHERE id = ? AND project_id = ?',
        [input.epicId, context.projectId || 'default']
      );

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

      // Build update query
      const updates: string[] = [];
      const params: any[] = [];

      if (input.title !== undefined) {
        updates.push('title = ?');
        params.push(input.title);
      }
      if (input.description !== undefined) {
        updates.push('description = ?');
        params.push(input.description);
      }
      if (input.goals !== undefined) {
        updates.push('goals = ?');
        params.push(JSON.stringify(input.goals));
      }
      if (input.priority !== undefined) {
        updates.push('priority = ?');
        params.push(input.priority);
      }
      if (input.owner !== undefined) {
        updates.push('owner = ?');
        params.push(input.owner);
      }
      if (input.targetDate !== undefined) {
        updates.push('target_date = ?');
        params.push(input.targetDate);
      }
      if (input.successCriteria !== undefined) {
        updates.push('success_criteria = ?');
        params.push(JSON.stringify(input.successCriteria));
      }
      if (input.repositories !== undefined) {
        updates.push('repositories = ?');
        params.push(JSON.stringify(input.repositories));
      }
      if (input.status !== undefined) {
        updates.push('status = ?');
        params.push(input.status);
      }

      updates.push('updated_at = ?');
      params.push(new Date().toISOString());

      params.push(input.epicId, context.projectId || 'default');

      const updateResult = await context.db.run(
        `UPDATE agile_epics SET ${updates.join(', ')} WHERE id = ? AND project_id = ?`,
        params
      );

      if (!updateResult.success) {
        return createErrorResult({
          code: 'DATABASE_ERROR',
          message: 'Failed to update epic',
          details: { error: updateResult.error },
          category: 'system'
        });
      }

      // Fetch updated epic
      const updatedResult = await context.db.get(
        'SELECT * FROM agile_epics WHERE id = ?',
        [input.epicId]
      );

      const epic = updatedResult.data;

      return createSuccessResult({
        epic: {
          id: epic.id,
          title: epic.title,
          description: epic.description,
          goals: JSON.parse(epic.goals || '[]'),
          priority: epic.priority,
          owner: epic.owner,
          targetDate: epic.target_date,
          successCriteria: JSON.parse(epic.success_criteria || '[]'),
          repositories: JSON.parse(epic.repositories || '[]'),
          status: epic.status,
          createdAt: new Date(epic.created_at).toISOString(),
          updatedAt: new Date(epic.updated_at).toISOString()
        },
        updated: true
      });

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

/**
 * Update a sprint
 */
const updateSprintTool = createTool<UpdateSprintInput, any>({
  name: 'update_sprint',
  description: 'Update an existing sprint',
  category: 'agile-management',
  inputSchema: {
    type: 'object',
    properties: {
      sprintId: {
        type: 'string',
        description: 'Sprint ID to update',
        pattern: '^[a-zA-Z0-9-]+$'
      },
      name: {
        type: 'string',
        description: 'New name for the sprint',
        minLength: 1,
        maxLength: 100
      },
      goal: {
        type: 'string',
        description: 'New sprint goal',
        maxLength: 500
      },
      status: {
        type: 'string',
        enum: ['active', 'completed', 'cancelled'],
        description: 'New status'
      },
      endDate: {
        type: 'string',
        format: 'date',
        description: 'New end date'
      },
      team: {
        type: 'array',
        description: 'Updated team members',
        items: { type: 'string' }
      }
    },
    required: ['sprintId'],
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: UpdateSprintInput, context: RequestContext) {
    try {
      // Check if sprint exists
      const sprintResult = await context.db.get(
        'SELECT * FROM agile_sprints WHERE id = ? AND project_id = ?',
        [input.sprintId, context.projectId || 'default']
      );

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

      // Build update query
      const updates: string[] = [];
      const params: any[] = [];

      if (input.name !== undefined) {
        updates.push('name = ?');
        params.push(input.name);
      }
      if (input.goal !== undefined) {
        updates.push('goal = ?');
        params.push(input.goal);
      }
      if (input.status !== undefined) {
        updates.push('status = ?');
        params.push(input.status);
      }
      if (input.endDate !== undefined) {
        updates.push('end_date = ?');
        params.push(input.endDate);
      }
      if (input.team !== undefined) {
        updates.push('team_members = ?');
        params.push(JSON.stringify(input.team));
      }

      updates.push('updated_at = ?');
      params.push(new Date().toISOString());

      params.push(input.sprintId, context.projectId || 'default');

      const updateResult = await context.db.run(
        `UPDATE agile_sprints SET ${updates.join(', ')} WHERE id = ? AND project_id = ?`,
        params
      );

      if (!updateResult.success) {
        return createErrorResult({
          code: 'DATABASE_ERROR',
          message: 'Failed to update sprint',
          details: { error: updateResult.error },
          category: 'system'
        });
      }

      // Fetch updated sprint
      const updatedResult = await context.db.get(
        'SELECT * FROM agile_sprints WHERE id = ?',
        [input.sprintId]
      );

      const sprint = updatedResult.data;

      return createSuccessResult({
        sprint: {
          id: sprint.id,
          name: sprint.name,
          goal: sprint.goal,
          status: sprint.status,
          startDate: new Date(sprint.start_date).toISOString(),
          endDate: new Date(sprint.end_date).toISOString(),
          team: JSON.parse(sprint.team_members || '[]'),
          createdAt: new Date(sprint.created_at).toISOString(),
          updatedAt: new Date(sprint.updated_at).toISOString()
        },
        updated: true
      });

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

/**
 * Update a story (alias for update_agile_story)
 */
const updateStoryTool = createTool<UpdateStoryInput, any>({
  name: 'update_agile_story',
  description: 'Update an existing user story',
  category: 'agile-management',
  inputSchema: {
    type: 'object',
    properties: {
      storyId: {
        type: 'string',
        description: 'Story ID to update',
        pattern: '^[a-zA-Z0-9-]+$'
      },
      title: {
        type: 'string',
        description: 'New title',
        minLength: 1,
        maxLength: 200
      },
      description: {
        type: 'string',
        description: 'New description',
        maxLength: 2000
      },
      acceptanceCriteria: {
        type: 'array',
        description: 'Updated acceptance criteria',
        items: { type: 'string' }
      },
      storyPoints: {
        type: 'integer',
        description: 'New story points estimate',
        minimum: 0,
        maximum: 100
      },
      priority: {
        type: 'string',
        enum: ['low', 'medium', 'high', 'critical'],
        description: 'New priority'
      },
      tags: {
        type: 'array',
        description: 'Updated tags',
        items: { type: 'string' }
      },
      assignedTo: {
        type: 'string',
        description: 'New assignee',
        maxLength: 100
      },
      epicId: {
        type: 'string',
        description: 'New epic ID',
        pattern: '^[a-zA-Z0-9-]+$'
      },
      status: {
        type: 'string',
        enum: ['todo', 'in_progress', 'review', 'done'],
        description: 'New status'
      }
    },
    required: ['storyId'],
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: UpdateStoryInput, context: RequestContext) {
    try {
      // Check if story exists
      const storyResult = await context.db.get(
        'SELECT * FROM agile_stories WHERE id = ? AND project_id = ?',
        [input.storyId, context.projectId || 'default']
      );

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

      // Build update query
      const updates: string[] = [];
      const params: any[] = [];

      if (input.title !== undefined) {
        updates.push('title = ?');
        params.push(input.title);
      }
      if (input.description !== undefined) {
        updates.push('description = ?');
        params.push(input.description);
      }
      if (input.acceptanceCriteria !== undefined) {
        updates.push('acceptance_criteria = ?');
        params.push(JSON.stringify(input.acceptanceCriteria));
      }
      if (input.storyPoints !== undefined) {
        updates.push('story_points = ?');
        params.push(input.storyPoints);
      }
      if (input.priority !== undefined) {
        updates.push('priority = ?');
        params.push(input.priority);
      }
      if (input.tags !== undefined) {
        updates.push('tags = ?');
        params.push(JSON.stringify(input.tags));
      }
      if (input.assignedTo !== undefined) {
        updates.push('assigned_to = ?');
        params.push(input.assignedTo);
      }
      if (input.epicId !== undefined) {
        updates.push('epic_id = ?');
        params.push(input.epicId);
      }
      if (input.status !== undefined) {
        updates.push('status = ?');
        params.push(input.status);
      }

      updates.push('updated_at = ?');
      params.push(new Date().toISOString());

      params.push(input.storyId, context.projectId || 'default');

      const updateResult = await context.db.run(
        `UPDATE agile_stories SET ${updates.join(', ')} WHERE id = ? AND project_id = ?`,
        params
      );

      if (!updateResult.success) {
        return createErrorResult({
          code: 'DATABASE_ERROR',
          message: 'Failed to update story',
          details: { error: updateResult.error },
          category: 'system'
        });
      }

      // Fetch updated story
      const updatedResult = await context.db.get(
        'SELECT * FROM agile_stories WHERE id = ?',
        [input.storyId]
      );

      const story = updatedResult.data;

      return createSuccessResult({
        story: {
          id: story.id,
          title: story.title,
          description: story.description,
          acceptanceCriteria: JSON.parse(story.acceptance_criteria || '[]'),
          storyPoints: story.story_points,
          priority: story.priority,
          status: story.status,
          tags: JSON.parse(story.tags || '[]'),
          assignedTo: story.assigned_to,
          sprintId: story.sprint_id,
          epicId: story.epic_id,
          createdAt: new Date(story.created_at).toISOString(),
          updatedAt: new Date(story.updated_at).toISOString()
        },
        updated: true
      });

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

/**
 * Get a story
 */
const getStoryTool = createTool<GetStoryInput, any>({
  name: 'get_story',
  description: 'Get details of a specific user story',
  category: 'agile-management',
  readOnly: true,
  inputSchema: {
    type: 'object',
    properties: {
      storyId: {
        type: 'string',
        description: 'The story ID to retrieve',
        pattern: '^[a-zA-Z0-9-]+$'
      }
    },
    required: ['storyId'],
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: GetStoryInput, context: RequestContext) {
    try {
      const result = await context.db.get(
        'SELECT * FROM agile_stories WHERE id = ? AND project_id = ?',
        [input.storyId, context.projectId || 'default']
      );

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

      const story = result.data;

      return createSuccessResult({
        story: {
          id: story.id,
          title: story.title,
          description: story.description,
          acceptanceCriteria: JSON.parse(story.acceptance_criteria || '[]'),
          storyPoints: story.story_points,
          priority: story.priority,
          status: story.status,
          tags: JSON.parse(story.tags || '[]'),
          assignedTo: story.assigned_to,
          sprintId: story.sprint_id,
          epicId: story.epic_id,
          createdAt: new Date(story.created_at).toISOString(),
          updatedAt: new Date(story.updated_at).toISOString()
        }
      });

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

/**
 * Assign story to sprint (alias for assign_story_to_sprint)
 */
const assignStoryToSprintTool = createTool<AssignStoryToSprintInput, any>({
  name: 'assign_story_to_sprint',
  description: 'Assign a user story to a specific sprint',
  category: 'agile-management',
  inputSchema: {
    type: 'object',
    properties: {
      storyId: {
        type: 'string',
        description: 'Story ID to assign',
        pattern: '^[a-zA-Z0-9-]+$'
      },
      sprintId: {
        type: 'string',
        description: 'Sprint ID to assign the story to',
        pattern: '^[a-zA-Z0-9-]+$'
      }
    },
    required: ['storyId', 'sprintId'],
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: AssignStoryToSprintInput, context: RequestContext) {
    try {
      // Verify story exists
      const storyResult = await context.db.get(
        'SELECT * FROM agile_stories WHERE id = ? AND project_id = ?',
        [input.storyId, context.projectId || 'default']
      );

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

      // Verify sprint exists
      const sprintResult = await context.db.get(
        'SELECT * FROM agile_sprints WHERE id = ? AND project_id = ?',
        [input.sprintId, context.projectId || 'default']
      );

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

      // Update story with sprint assignment
      const updateResult = await context.db.run(
        'UPDATE agile_stories SET sprint_id = ?, updated_at = ? WHERE id = ? AND project_id = ?',
        [input.sprintId, new Date().toISOString(), input.storyId, context.projectId || 'default']
      );

      if (!updateResult.success) {
        return createErrorResult({
          code: 'DATABASE_ERROR',
          message: 'Failed to assign story to sprint',
          details: { error: updateResult.error },
          category: 'system'
        });
      }

      return createSuccessResult({
        assigned: true,
        storyId: input.storyId,
        sprintId: input.sprintId,
        story: storyResult.data,
        sprint: sprintResult.data
      });

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

/**
 * Record daily standup
 */
const recordStandupTool = createTool<RecordStandupInput, any>({
  name: 'record_daily_standup',
  description: 'Record daily standup updates for a team member',
  category: 'agile-management',
  inputSchema: {
    type: 'object',
    properties: {
      sprintId: {
        type: 'string',
        description: 'Sprint ID for the standup',
        pattern: '^[a-zA-Z0-9-]+$'
      },
      teamMember: {
        type: 'string',
        description: 'Team member name',
        minLength: 1,
        maxLength: 100
      },
      yesterday: {
        type: 'string',
        description: 'What was accomplished yesterday',
        minLength: 1,
        maxLength: 1000
      },
      today: {
        type: 'string',
        description: 'What will be worked on today',
        minLength: 1,
        maxLength: 1000
      },
      blockers: {
        type: 'array',
        description: 'Any blockers or impediments',
        items: { type: 'string' }
      },
      mood: {
        type: 'string',
        enum: ['great', 'good', 'okay', 'struggling'],
        description: 'Team member mood/sentiment'
      }
    },
    required: ['sprintId', 'teamMember', 'yesterday', 'today', 'blockers'],
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: RecordStandupInput, context: RequestContext) {
    try {
      // Verify sprint exists
      const sprintResult = await context.db.get(
        'SELECT * FROM agile_sprints WHERE id = ? AND project_id = ?',
        [input.sprintId, context.projectId || 'default']
      );

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

      const id = randomUUID();
      const now = new Date().toISOString();

      const result = await context.db.run(
        `INSERT INTO standups (
          id, project_id, sprint_id, team_member, 
          yesterday, today, blockers, mood, 
          created_at, updated_at
        ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
        [
          id,
          context.projectId || 'default',
          input.sprintId,
          input.teamMember,
          input.yesterday,
          input.today,
          JSON.stringify(input.blockers),
          input.mood || 'okay',
          now,
          now
        ]
      );

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

      return createSuccessResult({
        standup: {
          id,
          sprintId: input.sprintId,
          teamMember: input.teamMember,
          yesterday: input.yesterday,
          today: input.today,
          blockers: input.blockers,
          mood: input.mood || 'okay',
          createdAt: now
        },
        recorded: true
      });

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

/**
 * Generate burndown chart data
 */
const generateBurndownTool = createTool<GenerateBurndownInput, any>({
  name: 'generate_burndown_chart',
  description: 'Generate burndown chart data for a sprint',
  category: 'agile-management',
  readOnly: true,
  inputSchema: {
    type: 'object',
    properties: {
      sprintId: {
        type: 'string',
        description: 'Sprint ID to generate burndown for',
        pattern: '^[a-zA-Z0-9-]+$'
      }
    },
    required: ['sprintId'],
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: GenerateBurndownInput, context: RequestContext) {
    try {
      // Get sprint details
      const sprintResult = await context.db.get(
        'SELECT * FROM agile_sprints WHERE id = ? AND project_id = ?',
        [input.sprintId, context.projectId || 'default']
      );

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

      const sprint = sprintResult.data;
      const startDate = new Date(sprint.start_date);
      const endDate = new Date(sprint.end_date);
      const totalDays = Math.ceil((endDate.getTime() - startDate.getTime()) / (1000 * 60 * 60 * 24));

      // Get total story points for the sprint
      const totalPointsResult = await context.db.get(
        'SELECT SUM(story_points) as total_points FROM agile_stories WHERE sprint_id = ?',
        [input.sprintId]
      );

      const totalPoints = totalPointsResult.data?.total_points || 0;

      // Get story completion history
      const historyResult = await context.db.query(
        `SELECT 
          DATE(updated_at) as completion_date,
          SUM(story_points) as completed_points
        FROM agile_stories 
        WHERE sprint_id = ? AND status = 'done'
        GROUP BY DATE(updated_at)
        ORDER BY completion_date`,
        [input.sprintId]
      );

      // Generate ideal burndown line
      const idealBurndown = [];
      for (let i = 0; i <= totalDays; i++) {
        const date = new Date(startDate);
        date.setDate(date.getDate() + i);
        idealBurndown.push({
          date: date.toISOString().split('T')[0],
          remainingPoints: totalPoints - (totalPoints / totalDays) * i
        });
      }

      // Generate actual burndown based on completion history
      const actualBurndown = [];
      let cumulativeCompleted = 0;
      const completionMap = new Map();

      // Build completion map
      if (historyResult.success && historyResult.data) {
        historyResult.data.forEach((row: any) => {
          completionMap.set(row.completion_date, row.completed_points);
        });
      }

      // Generate actual burndown data
      for (let i = 0; i <= totalDays; i++) {
        const date = new Date(startDate);
        date.setDate(date.getDate() + i);
        const dateStr = date.toISOString().split('T')[0];

        if (completionMap.has(dateStr)) {
          cumulativeCompleted += completionMap.get(dateStr);
        }

        actualBurndown.push({
          date: dateStr,
          remainingPoints: totalPoints - cumulativeCompleted
        });

        // Stop at today's date
        if (date >= new Date()) break;
      }

      return createSuccessResult({
        sprint: {
          id: sprint.id,
          name: sprint.name,
          startDate: startDate.toISOString(),
          endDate: endDate.toISOString(),
          totalDays,
          totalPoints
        },
        burndown: {
          ideal: idealBurndown,
          actual: actualBurndown,
          currentRemaining: totalPoints - cumulativeCompleted,
          completedPoints: cumulativeCompleted,
          completionPercentage: totalPoints > 0 ? (cumulativeCompleted / totalPoints) * 100 : 0
        }
      });

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

/**
 * Generate velocity report
 */
const generateVelocityTool = createTool<GenerateVelocityInput, any>({
  name: 'generate_velocity_report',
  description: 'Generate team velocity report across multiple sprints',
  category: 'agile-management',
  readOnly: true,
  inputSchema: {
    type: 'object',
    properties: {
      teamId: {
        type: 'string',
        description: 'Team ID to generate report for (optional)',
        maxLength: 100
      },
      numberOfSprints: {
        type: 'integer',
        description: 'Number of recent sprints to include',
        minimum: 1,
        maximum: 20,
        default: 5
      }
    },
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: GenerateVelocityInput, context: RequestContext) {
    try {
      // Get recent completed sprints
      const sprintsResult = await context.db.query(
        `SELECT * FROM agile_sprints 
        WHERE project_id = ? AND status = 'completed'
        ORDER BY end_date DESC 
        LIMIT ?`,
        [context.projectId || 'default', input.numberOfSprints || 5]
      );

      if (!sprintsResult.success || !sprintsResult.data || sprintsResult.data.length === 0) {
        return createErrorResult({
          code: 'RESOURCE_NOT_FOUND',
          message: 'No completed sprints found',
          category: 'validation'
        });
      }

      const velocityData = [];
      let totalCompleted = 0;
      let totalCommitted = 0;

      for (const sprint of sprintsResult.data) {
        // Get story points for this sprint
        const statsResult = await context.db.get(
          `SELECT 
            SUM(story_points) as committed_points,
            SUM(CASE WHEN status = 'done' THEN story_points ELSE 0 END) as completed_points
          FROM agile_stories 
          WHERE sprint_id = ?`,
          [sprint.id]
        );

        const committed = statsResult.data?.committed_points || 0;
        const completed = statsResult.data?.completed_points || 0;

        totalCommitted += committed;
        totalCompleted += completed;

        velocityData.push({
          sprintId: sprint.id,
          sprintName: sprint.name,
          startDate: new Date(sprint.start_date).toISOString(),
          endDate: new Date(sprint.end_date).toISOString(),
          committedPoints: committed,
          completedPoints: completed,
          velocityPercentage: committed > 0 ? (completed / committed) * 100 : 0
        });
      }

      const averageVelocity = velocityData.length > 0 ? totalCompleted / velocityData.length : 0;
      const averageCommitment = velocityData.length > 0 ? totalCommitted / velocityData.length : 0;

      return createSuccessResult({
        report: {
          numberOfSprints: velocityData.length,
          averageVelocity: Math.round(averageVelocity * 100) / 100,
          averageCommitment: Math.round(averageCommitment * 100) / 100,
          totalCompleted,
          totalCommitted,
          completionRate: totalCommitted > 0 ? (totalCompleted / totalCommitted) * 100 : 0
        },
        sprints: velocityData,
        trend: {
          improving: velocityData.length >= 3 && 
            velocityData[0].completedPoints > velocityData[velocityData.length - 1].completedPoints,
          lastThreeSprints: velocityData.slice(0, 3).map(s => s.completedPoints)
        }
      });

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

/**
 * Get agile epic (alias for get_epic)
 */
const getAgileEpicTool = createTool<GetEpicInput, any>({
  name: 'get_agile_epic',
  description: 'Get details of a specific epic (agile alias)',
  category: 'agile-management',
  readOnly: true,
  inputSchema: {
    type: 'object',
    properties: {
      epicId: {
        type: 'string',
        description: 'Epic ID to retrieve',
        pattern: '^[a-zA-Z0-9-]+$'
      }
    },
    required: ['epicId'],
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: GetEpicInput, context: RequestContext) {
    // Reuse the getEpicTool logic
    return getEpicTool.execute(input, context);
  }
});

/**
 * Get agile sprint (alias for get_sprint)
 */
const getAgileSprintTool = createTool<GetSprintInput, any>({
  name: 'get_agile_sprint',
  description: 'Get details of a specific sprint (agile alias)',
  category: 'agile-management',
  readOnly: true,
  inputSchema: {
    type: 'object',
    properties: {
      sprintId: {
        type: 'string',
        description: 'Sprint ID to retrieve',
        pattern: '^[a-zA-Z0-9-]+$'
      }
    },
    required: ['sprintId'],
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: GetSprintInput, context: RequestContext) {
    // Reuse the getSprintTool logic
    return getSprintTool.execute(input, context);
  }
});

/**
 * Complete a sprint with proper closure and carryover handling
 */
const completeSprintTool = createTool<CompleteSprintInput, any>({
  name: 'complete_sprint',
  description: 'Complete a sprint with carryover story handling and final metrics',
  category: 'agile-management',
  inputSchema: {
    type: 'object',
    properties: {
      sprintId: {
        type: 'string',
        description: 'Sprint ID to complete',
        pattern: '^[a-zA-Z0-9-]+$'
      },
      carryoverStories: {
        type: 'array',
        description: 'Story IDs to carry over to next sprint',
        items: { type: 'string' },
        default: []
      },
      completionNotes: {
        type: 'string',
        description: 'Sprint completion notes and retrospective summary'
      },
      completedBy: {
        type: 'string',
        description: 'Person completing the sprint'
      }
    },
    required: ['sprintId'],
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: CompleteSprintInput, context: RequestContext) {
    try {
      const db = context.db;
      const now = Math.floor(Date.now() / 1000);
      const completionId = randomUUID();

      // Get sprint details
      const sprintResult = await db.get(
        'SELECT * FROM agile_sprints WHERE id = ? AND project_id = ?',
        [input.sprintId, context.projectId || 'default']
      );

      if (!sprintResult.success || !sprintResult.data) {
        return createErrorResult({
          code: 'NOT_FOUND',
          message: 'Sprint not found',
          category: 'validation'
        });
      }

      const sprint = sprintResult.data;

      // Get all stories in sprint
      const storiesResult = await db.query(
        'SELECT * FROM agile_stories WHERE sprint_id = ? AND project_id = ?',
        [input.sprintId, context.projectId || 'default']
      );

      const stories = storiesResult.data || [];
      const completedStories = stories.filter(s => s.status === 'done');
      const carryoverStories = input.carryoverStories || [];

      // Calculate final metrics
      const velocityAchieved = completedStories.reduce((sum, s) => sum + (s.story_points || 0), 0);
      const storiesCompleted = completedStories.length;
      const storiesCarriedOver = carryoverStories.length;

      // Update sprint status to completed
      await db.run(
        `UPDATE agile_sprints SET 
         status = 'completed', 
         story_points_completed = ?, 
         stories_completed = ?, 
         velocity = ?, 
         updated_at = ? 
         WHERE id = ?`,
        [velocityAchieved, storiesCompleted, velocityAchieved, now, input.sprintId]
      );

      // Record sprint completion
      await db.run(
        `INSERT INTO agile_sprint_completions 
         (id, project_id, sprint_id, carryover_stories, completion_notes, 
          velocity_achieved, story_points_completed, stories_completed, 
          stories_carried_over, completed_by, completed_at)
         VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
        [
          completionId,
          context.projectId || 'default',
          input.sprintId,
          JSON.stringify(carryoverStories),
          input.completionNotes || '',
          velocityAchieved,
          velocityAchieved,
          storiesCompleted,
          storiesCarriedOver,
          input.completedBy || 'system',
          now
        ]
      );

      // Move carryover stories to backlog or next sprint
      if (carryoverStories.length > 0) {
        await db.run(
          `UPDATE agile_stories SET sprint_id = NULL, status = 'todo', updated_at = ? 
           WHERE id IN (${carryoverStories.map(() => '?').join(',')}) AND project_id = ?`,
          [...carryoverStories, now, context.projectId || 'default']
        );
      }

      return createSuccessResult({
        sprintId: input.sprintId,
        completionId,
        summary: {
          velocityAchieved,
          storiesCompleted,
          storiesCarriedOver,
          totalStories: stories.length
        },
        carryoverStories,
        completedAt: new Date(now * 1000).toISOString(),
        completedBy: input.completedBy || 'system'
      });

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

/**
 * Block a story with specific reason and blocker type
 */
const blockStoryTool = createTool<BlockStoryInput, any>({
  name: 'block_story',
  description: 'Block a story due to dependency, external factor, or technical issue',
  category: 'agile-management',
  inputSchema: {
    type: 'object',
    properties: {
      storyId: {
        type: 'string',
        description: 'Story ID to block',
        pattern: '^[a-zA-Z0-9-]+$'
      },
      blockerType: {
        type: 'string',
        enum: ['dependency', 'external', 'technical', 'resource'],
        description: 'Type of blocker'
      },
      reason: {
        type: 'string',
        description: 'Detailed reason for blocking the story',
        minLength: 5
      },
      blockerStoryId: {
        type: 'string',
        description: 'ID of story that is blocking this one (for dependency type)',
        pattern: '^[a-zA-Z0-9-]+$'
      }
    },
    required: ['storyId', 'blockerType', 'reason'],
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: BlockStoryInput, context: RequestContext) {
    try {
      const db = context.db;
      const blockerId = randomUUID();
      const now = Math.floor(Date.now() / 1000);

      // Verify story exists
      const storyResult = await db.get(
        'SELECT * FROM agile_stories WHERE id = ? AND project_id = ?',
        [input.storyId, context.projectId || 'default']
      );

      if (!storyResult.success || !storyResult.data) {
        return createErrorResult({
          code: 'NOT_FOUND',
          message: 'Story not found',
          category: 'validation'
        });
      }

      // Verify blocker story exists if provided
      if (input.blockerStoryId) {
        const blockerStoryResult = await db.get(
          'SELECT * FROM agile_stories WHERE id = ? AND project_id = ?',
          [input.blockerStoryId, context.projectId || 'default']
        );

        if (!blockerStoryResult.success || !blockerStoryResult.data) {
          return createErrorResult({
            code: 'NOT_FOUND',
            message: 'Blocker story not found',
            category: 'validation'
          });
        }
      }

      // Create blocker record
      await db.run(
        `INSERT INTO agile_story_blockers 
         (id, project_id, blocked_story_id, blocker_story_id, blocker_type, reason, status, created_at, updated_at)
         VALUES (?, ?, ?, ?, ?, ?, 'active', ?, ?)`,
        [
          blockerId,
          context.projectId || 'default',
          input.storyId,
          input.blockerStoryId || null,
          input.blockerType,
          input.reason,
          now,
          now
        ]
      );

      // Update story status if not already blocked
      const story = storyResult.data;
      if (story.status !== 'blocked') {
        await db.run(
          'UPDATE agile_stories SET status = ?, updated_at = ? WHERE id = ?',
          ['blocked', now, input.storyId]
        );
      }

      return createSuccessResult({
        blockerId,
        storyId: input.storyId,
        blockerType: input.blockerType,
        reason: input.reason,
        blockerStoryId: input.blockerStoryId || null,
        createdAt: new Date(now * 1000).toISOString()
      });

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

/**
 * Unblock a story by resolving a specific blocker
 */
const unblockStoryTool = createTool<UnblockStoryInput, any>({
  name: 'unblock_story',
  description: 'Unblock a story by resolving a specific blocker',
  category: 'agile-management',
  inputSchema: {
    type: 'object',
    properties: {
      blockerId: {
        type: 'string',
        description: 'Blocker ID to resolve',
        pattern: '^[a-zA-Z0-9-]+$'
      },
      resolution: {
        type: 'string',
        description: 'How the blocker was resolved',
        minLength: 5
      },
      resolvedBy: {
        type: 'string',
        description: 'Person who resolved the blocker'
      }
    },
    required: ['blockerId', 'resolution'],
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: UnblockStoryInput, context: RequestContext) {
    try {
      const db = context.db;
      const now = Math.floor(Date.now() / 1000);

      // Get blocker details
      const blockerResult = await db.get(
        'SELECT * FROM agile_story_blockers WHERE id = ? AND project_id = ?',
        [input.blockerId, context.projectId || 'default']
      );

      if (!blockerResult.success || !blockerResult.data) {
        return createErrorResult({
          code: 'NOT_FOUND',
          message: 'Blocker not found',
          category: 'validation'
        });
      }

      const blocker = blockerResult.data;

      // Update blocker status
      await db.run(
        `UPDATE agile_story_blockers SET 
         status = 'resolved', 
         resolution = ?, 
         resolved_by = ?, 
         resolved_at = ?, 
         updated_at = ?
         WHERE id = ?`,
        [input.resolution, input.resolvedBy || 'system', now, now, input.blockerId]
      );

      // Check if story has any remaining active blockers
      const remainingBlockersResult = await db.query(
        'SELECT COUNT(*) as count FROM agile_story_blockers WHERE blocked_story_id = ? AND status = "active" AND project_id = ?',
        [blocker.blocked_story_id, context.projectId || 'default']
      );

      const hasRemainingBlockers = remainingBlockersResult.data?.[0]?.count > 0;

      // If no remaining blockers, update story status back to todo
      if (!hasRemainingBlockers) {
        await db.run(
          'UPDATE agile_stories SET status = ?, updated_at = ? WHERE id = ? AND status = "blocked"',
          ['todo', now, blocker.blocked_story_id]
        );
      }

      return createSuccessResult({
        blockerId: input.blockerId,
        storyId: blocker.blocked_story_id,
        resolution: input.resolution,
        resolvedBy: input.resolvedBy || 'system',
        resolvedAt: new Date(now * 1000).toISOString(),
        storyUnblocked: !hasRemainingBlockers
      });

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

/**
 * Assign a story to a team member
 */
const assignStoryTool = createTool<AssignStoryInput, any>({
  name: 'assign_story',
  description: 'Assign a story to a specific team member',
  category: 'agile-management',
  inputSchema: {
    type: 'object',
    properties: {
      storyId: {
        type: 'string',
        description: 'Story ID to assign',
        pattern: '^[a-zA-Z0-9-]+$'
      },
      assignee: {
        type: 'string',
        description: 'Team member to assign the story to',
        minLength: 1
      },
      notes: {
        type: 'string',
        description: 'Optional assignment notes'
      }
    },
    required: ['storyId', 'assignee'],
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: AssignStoryInput, context: RequestContext) {
    try {
      const db = context.db;
      const now = Math.floor(Date.now() / 1000);

      // Verify story exists
      const storyResult = await db.get(
        'SELECT * FROM agile_stories WHERE id = ? AND project_id = ?',
        [input.storyId, context.projectId || 'default']
      );

      if (!storyResult.success || !storyResult.data) {
        return createErrorResult({
          code: 'NOT_FOUND',
          message: 'Story not found',
          category: 'validation'
        });
      }

      const story = storyResult.data;

      // Update story assignee
      await db.run(
        'UPDATE agile_stories SET assignee = ?, updated_at = ? WHERE id = ?',
        [input.assignee, now, input.storyId]
      );

      // Add assignment comment if notes provided
      if (input.notes) {
        const commentId = randomUUID();
        await db.run(
          `INSERT INTO agile_story_comments 
           (id, project_id, story_id, author, content, created_at, updated_at)
           VALUES (?, ?, ?, ?, ?, ?, ?)`,
          [
            commentId,
            context.projectId || 'default',
            input.storyId,
            'system',
            `Story assigned to ${input.assignee}. ${input.notes}`,
            now,
            now
          ]
        );
      }

      return createSuccessResult({
        storyId: input.storyId,
        assignee: input.assignee,
        previousAssignee: story.assignee || null,
        assignedAt: new Date(now * 1000).toISOString(),
        notes: input.notes || null
      });

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

/**
 * Add a comment to a story
 */
const addStoryCommentTool = createTool<AddStoryCommentInput, any>({
  name: 'add_story_comment',
  description: 'Add a comment to a story for team collaboration',
  category: 'agile-management',
  inputSchema: {
    type: 'object',
    properties: {
      storyId: {
        type: 'string',
        description: 'Story ID to comment on',
        pattern: '^[a-zA-Z0-9-]+$'
      },
      content: {
        type: 'string',
        description: 'Comment content',
        minLength: 1
      },
      author: {
        type: 'string',
        description: 'Comment author',
        minLength: 1
      },
      threadId: {
        type: 'string',
        description: 'Thread ID for grouping related comments'
      },
      parentCommentId: {
        type: 'string',
        description: 'Parent comment ID for replies'
      }
    },
    required: ['storyId', 'content', 'author'],
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: AddStoryCommentInput, context: RequestContext) {
    try {
      const db = context.db;
      const commentId = randomUUID();
      const now = Math.floor(Date.now() / 1000);

      // Verify story exists
      const storyResult = await db.get(
        'SELECT * FROM agile_stories WHERE id = ? AND project_id = ?',
        [input.storyId, context.projectId || 'default']
      );

      if (!storyResult.success || !storyResult.data) {
        return createErrorResult({
          code: 'NOT_FOUND',
          message: 'Story not found',
          category: 'validation'
        });
      }

      // Verify parent comment exists if provided
      if (input.parentCommentId) {
        const parentResult = await db.get(
          'SELECT * FROM agile_story_comments WHERE id = ? AND story_id = ? AND project_id = ?',
          [input.parentCommentId, input.storyId, context.projectId || 'default']
        );

        if (!parentResult.success || !parentResult.data) {
          return createErrorResult({
            code: 'NOT_FOUND',
            message: 'Parent comment not found',
            category: 'validation'
          });
        }
      }

      // Generate thread ID if not provided and no parent
      const threadId = input.threadId || (input.parentCommentId ? null : commentId);

      // Add comment
      await db.run(
        `INSERT INTO agile_story_comments 
         (id, project_id, story_id, author, content, thread_id, parent_comment_id, created_at, updated_at)
         VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
        [
          commentId,
          context.projectId || 'default',
          input.storyId,
          input.author,
          input.content,
          threadId,
          input.parentCommentId || null,
          now,
          now
        ]
      );

      return createSuccessResult({
        commentId,
        storyId: input.storyId,
        author: input.author,
        content: input.content,
        threadId,
        parentCommentId: input.parentCommentId || null,
        createdAt: new Date(now * 1000).toISOString()
      });

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

/**
 * Split a story into multiple smaller stories
 */
const splitStoryTool = createTool<SplitStoryInput, any>({
  name: 'split_story',
  description: 'Split a large story into multiple smaller, manageable stories',
  category: 'agile-management',
  inputSchema: {
    type: 'object',
    properties: {
      storyId: {
        type: 'string',
        description: 'ID of the story to split',
        pattern: '^[a-zA-Z0-9-]+$'
      },
      newStories: {
        type: 'array',
        description: 'Array of new stories to create',
        items: {
          type: 'object',
          properties: {
            title: {
              type: 'string',
              description: 'Title of the new story',
              minLength: 1
            },
            description: {
              type: 'string',
              description: 'Description of the new story'
            },
            acceptanceCriteria: {
              type: 'array',
              items: { type: 'string' },
              description: 'Acceptance criteria for the new story'
            },
            storyPoints: {
              type: 'integer',
              description: 'Story points for the new story',
              minimum: 1,
              maximum: 13
            },
            priority: {
              type: 'string',
              enum: ['low', 'medium', 'high', 'critical'],
              description: 'Priority of the new story'
            }
          },
          required: ['title'],
          additionalProperties: false
        },
        minItems: 2
      },
      notes: {
        type: 'string',
        description: 'Notes about why the story was split'
      }
    },
    required: ['storyId', 'newStories'],
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: SplitStoryInput, context: RequestContext) {
    try {
      const db = context.db;
      const now = Math.floor(Date.now() / 1000);

      // Get the original story
      const originalResult = await db.get(
        'SELECT * FROM agile_stories WHERE id = ? AND project_id = ?',
        [input.storyId, context.projectId || 'default']
      );

      if (!originalResult.success || !originalResult.data) {
        return createErrorResult({
          code: 'NOT_FOUND',
          message: 'Original story not found',
          category: 'validation'
        });
      }

      const originalStory = originalResult.data;
      const createdStories = [];

      // Create new stories
      for (const newStoryData of input.newStories) {
        const newStoryId = randomUUID();
        
        await db.run(
          `INSERT INTO agile_stories 
           (id, project_id, sprint_id, epic_id, title, description, status, story_points, 
            priority, assignee, tags, acceptance_criteria, created_at, updated_at)
           VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
          [
            newStoryId,
            context.projectId || 'default',
            originalStory.sprint_id,
            originalStory.epic_id,
            newStoryData.title,
            newStoryData.description || '',
            'todo',
            newStoryData.storyPoints || 1,
            newStoryData.priority || originalStory.priority,
            originalStory.assignee,
            originalStory.tags,
            JSON.stringify(newStoryData.acceptanceCriteria || []),
            now,
            now
          ]
        );

        createdStories.push({
          id: newStoryId,
          title: newStoryData.title,
          description: newStoryData.description || '',
          storyPoints: newStoryData.storyPoints || 1,
          priority: newStoryData.priority || originalStory.priority
        });
      }

      // Update original story status to 'split'
      await db.run(
        'UPDATE agile_stories SET status = ?, updated_at = ? WHERE id = ? AND project_id = ?',
        ['split', now, input.storyId, context.projectId || 'default']
      );

      // Add a comment to the original story about the split
      const commentId = randomUUID();
      await db.run(
        `INSERT INTO agile_story_comments 
         (id, project_id, story_id, author, content, created_at, updated_at)
         VALUES (?, ?, ?, ?, ?, ?, ?)`,
        [
          commentId,
          context.projectId || 'default',
          input.storyId,
          'system',
          `Story split into ${input.newStories.length} new stories. ${input.notes || ''}`,
          now,
          now
        ]
      );

      return createSuccessResult({
        originalStoryId: input.storyId,
        originalTitle: originalStory.title,
        newStories: createdStories,
        splitAt: new Date(now * 1000).toISOString(),
        notes: input.notes || null
      });

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

/**
 * Link a story to an epic
 */
const linkStoryToEpicTool = createTool<LinkStoryToEpicInput, any>({
  name: 'link_story_to_epic',
  description: 'Link a story to an epic for better organization and tracking',
  category: 'agile-management',
  inputSchema: {
    type: 'object',
    properties: {
      storyId: {
        type: 'string',
        description: 'ID of the story to link',
        pattern: '^[a-zA-Z0-9-]+$'
      },
      epicId: {
        type: 'string',
        description: 'ID of the epic to link to',
        pattern: '^[a-zA-Z0-9-]+$'
      },
      notes: {
        type: 'string',
        description: 'Notes about why this story belongs to this epic'
      }
    },
    required: ['storyId', 'epicId'],
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: LinkStoryToEpicInput, context: RequestContext) {
    try {
      const db = context.db;
      const now = Math.floor(Date.now() / 1000);

      // Verify story exists
      const storyResult = await db.get(
        'SELECT * FROM agile_stories WHERE id = ? AND project_id = ?',
        [input.storyId, context.projectId || 'default']
      );

      if (!storyResult.success || !storyResult.data) {
        return createErrorResult({
          code: 'NOT_FOUND',
          message: 'Story not found',
          category: 'validation'
        });
      }

      // Verify epic exists
      const epicResult = await db.get(
        'SELECT * FROM agile_epics WHERE id = ? AND project_id = ?',
        [input.epicId, context.projectId || 'default']
      );

      if (!epicResult.success || !epicResult.data) {
        return createErrorResult({
          code: 'NOT_FOUND',
          message: 'Epic not found',
          category: 'validation'
        });
      }

      const story = storyResult.data;
      const epic = epicResult.data;
      const previousEpicId = story.epic_id;

      // Update story to link to epic
      await db.run(
        'UPDATE agile_stories SET epic_id = ?, updated_at = ? WHERE id = ? AND project_id = ?',
        [input.epicId, now, input.storyId, context.projectId || 'default']
      );

      // Add a comment about the epic linkage
      if (input.notes) {
        const commentId = randomUUID();
        await db.run(
          `INSERT INTO agile_story_comments 
           (id, project_id, story_id, author, content, created_at, updated_at)
           VALUES (?, ?, ?, ?, ?, ?, ?)`,
          [
            commentId,
            context.projectId || 'default',
            input.storyId,
            'system',
            `Story linked to epic "${epic.title}". ${input.notes}`,
            now,
            now
          ]
        );
      }

      return createSuccessResult({
        storyId: input.storyId,
        storyTitle: story.title,
        epicId: input.epicId,
        epicTitle: epic.title,
        previousEpicId: previousEpicId || null,
        linkedAt: new Date(now * 1000).toISOString(),
        notes: input.notes || null
      });

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

/**
 * Get epic progress with story completion metrics
 */
const getEpicProgressTool = createTool<GetEpicProgressInput, any>({
  name: 'get_epic_progress',
  description: 'Calculate epic completion progress based on linked stories',
  category: 'agile-management',
  inputSchema: {
    type: 'object',
    properties: {
      epicId: {
        type: 'string',
        description: 'Epic ID to analyze',
        pattern: '^[a-zA-Z0-9-]+$'
      }
    },
    required: ['epicId'],
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: GetEpicProgressInput, context: RequestContext) {
    try {
      const db = context.db;

      // Verify epic exists
      const epicResult = await db.get(
        'SELECT * FROM agile_epics WHERE id = ? AND project_id = ?',
        [input.epicId, context.projectId || 'default']
      );

      if (!epicResult.success || !epicResult.data) {
        return createErrorResult({
          code: 'NOT_FOUND',
          message: 'Epic not found',
          category: 'validation'
        });
      }

      const epic = epicResult.data;

      // Get all stories linked to this epic
      const storiesResult = await db.query(
        'SELECT * FROM agile_stories WHERE epic_id = ? AND project_id = ?',
        [input.epicId, context.projectId || 'default']
      );

      const stories = storiesResult.data || [];

      // Calculate progress metrics
      const totalStories = stories.length;
      const completedStories = stories.filter(s => s.status === 'done').length;
      const inProgressStories = stories.filter(s => s.status === 'in_progress').length;
      const blockedStories = stories.filter(s => s.status === 'blocked').length;

      const totalStoryPoints = stories.reduce((sum, s) => sum + (s.story_points || 0), 0);
      const completedStoryPoints = stories.filter(s => s.status === 'done')
        .reduce((sum, s) => sum + (s.story_points || 0), 0);

      const completionPercentage = totalStories > 0 ? Math.round((completedStories / totalStories) * 100) : 0;
      const storyPointsCompletionPercentage = totalStoryPoints > 0 ? 
        Math.round((completedStoryPoints / totalStoryPoints) * 100) : 0;

      // Get sprint breakdown
      const sprintBreakdown = await db.query(
        `SELECT s.id, s.name, COUNT(st.id) as story_count, 
         SUM(CASE WHEN st.status = 'done' THEN st.story_points ELSE 0 END) as completed_points,
         SUM(st.story_points) as total_points
         FROM agile_sprints s
         LEFT JOIN agile_stories st ON s.id = st.sprint_id AND st.epic_id = ?
         WHERE st.epic_id = ? AND s.project_id = ?
         GROUP BY s.id, s.name`,
        [input.epicId, input.epicId, context.projectId || 'default']
      );

      return createSuccessResult({
        epicId: input.epicId,
        epicTitle: epic.title,
        epicStatus: epic.status,
        progress: {
          totalStories,
          completedStories,
          inProgressStories,
          blockedStories,
          completionPercentage
        },
        storyPoints: {
          total: totalStoryPoints,
          completed: completedStoryPoints,
          completionPercentage: storyPointsCompletionPercentage
        },
        sprintBreakdown: sprintBreakdown.data || [],
        statusBreakdown: {
          todo: stories.filter(s => s.status === 'todo').length,
          in_progress: inProgressStories,
          review: stories.filter(s => s.status === 'review').length,
          done: completedStories,
          blocked: blockedStories
        },
        businessValue: epic.business_value,
        successCriteria: JSON.parse(epic.success_criteria || '[]')
      });

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

/**
 * Get epic timeline with story completion over time
 */
const getEpicTimelineTool = createTool<GetEpicTimelineInput, any>({
  name: 'get_epic_timeline',
  description: 'Generate epic delivery timeline with sprint breakdown and dependencies',
  category: 'agile-management',
  inputSchema: {
    type: 'object',
    properties: {
      epicId: {
        type: 'string',
        description: 'Epic ID to analyze',
        pattern: '^[a-zA-Z0-9-]+$'
      },
      includeDependencies: {
        type: 'boolean',
        description: 'Include dependency analysis in timeline',
        default: true
      }
    },
    required: ['epicId'],
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: GetEpicTimelineInput, context: RequestContext) {
    try {
      const db = context.db;

      // Verify epic exists
      const epicResult = await db.get(
        'SELECT * FROM agile_epics WHERE id = ? AND project_id = ?',
        [input.epicId, context.projectId || 'default']
      );

      if (!epicResult.success || !epicResult.data) {
        return createErrorResult({
          code: 'NOT_FOUND',
          message: 'Epic not found',
          category: 'validation'
        });
      }

      const epic = epicResult.data;

      // Get sprint timeline for this epic
      const timelineResult = await db.query(
        `SELECT 
           s.id as sprint_id,
           s.name as sprint_name,
           s.start_date,
           s.end_date,
           s.status as sprint_status,
           COUNT(st.id) as total_stories,
           SUM(CASE WHEN st.status = 'done' THEN 1 ELSE 0 END) as completed_stories,
           SUM(st.story_points) as total_points,
           SUM(CASE WHEN st.status = 'done' THEN st.story_points ELSE 0 END) as completed_points
         FROM agile_sprints s
         LEFT JOIN agile_stories st ON s.id = st.sprint_id AND st.epic_id = ?
         WHERE s.project_id = ? AND st.epic_id IS NOT NULL
         GROUP BY s.id, s.name, s.start_date, s.end_date, s.status
         ORDER BY s.start_date ASC`,
        [input.epicId, context.projectId || 'default']
      );

      const timeline = (timelineResult.data || []).map((sprint: any) => ({
        sprintId: sprint.sprint_id,
        sprintName: sprint.sprint_name,
        startDate: sprint.start_date ? new Date(sprint.start_date * 1000).toISOString() : null,
        endDate: sprint.end_date ? new Date(sprint.end_date * 1000).toISOString() : null,
        status: sprint.sprint_status,
        stories: {
          total: sprint.total_stories,
          completed: sprint.completed_stories,
          completionRate: sprint.total_stories > 0 ? 
            Math.round((sprint.completed_stories / sprint.total_stories) * 100) : 0
        },
        storyPoints: {
          total: sprint.total_points || 0,
          completed: sprint.completed_points || 0,
          completionRate: sprint.total_points > 0 ? 
            Math.round((sprint.completed_points / sprint.total_points) * 100) : 0
        }
      }));

      // Get dependencies if requested
      let dependencies = [];
      if (input.includeDependencies) {
        const depsResult = await db.query(
          `SELECT 
             sb.id as blocker_id,
             sb.blocked_story_id,
             sb.blocker_story_id,
             sb.blocker_type,
             sb.reason,
             sb.status as blocker_status,
             s1.title as blocked_story_title,
             s2.title as blocker_story_title
           FROM agile_story_blockers sb
           JOIN agile_stories s1 ON sb.blocked_story_id = s1.id
           LEFT JOIN agile_stories s2 ON sb.blocker_story_id = s2.id
           WHERE s1.epic_id = ? AND sb.project_id = ?`,
          [input.epicId, context.projectId || 'default']
        );

        dependencies = depsResult.data || [];
      }

      // Calculate estimated completion date
      const activeSprints = timeline.filter(t => t.status === 'active');
      const completedSprints = timeline.filter(t => t.status === 'completed');
      
      let estimatedCompletion = null;
      if (activeSprints.length > 0) {
        const lastActiveSprint = activeSprints[activeSprints.length - 1];
        estimatedCompletion = lastActiveSprint.endDate;
      }

      return createSuccessResult({
        epicId: input.epicId,
        epicTitle: epic.title,
        targetQuarter: epic.target_quarter,
        timeline,
        dependencies: input.includeDependencies ? dependencies : null,
        summary: {
          totalSprints: timeline.length,
          completedSprints: completedSprints.length,
          activeSprints: activeSprints.length,
          estimatedCompletion
        },
        businessValue: epic.business_value,
        risks: JSON.parse(epic.risks || '[]')
      });

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

/**
 * Close an epic with completion validation and metrics
 */
const closeEpicTool = createTool<CloseEpicInput, any>({
  name: 'close_epic',
  description: 'Formally close an epic with completion validation and business value assessment',
  category: 'agile-management',
  inputSchema: {
    type: 'object',
    properties: {
      epicId: {
        type: 'string',
        description: 'Epic ID to close',
        pattern: '^[a-zA-Z0-9-]+$'
      },
      completionNotes: {
        type: 'string',
        description: 'Notes about epic completion and outcomes'
      },
      businessValueRealized: {
        type: 'string',
        description: 'Assessment of business value actually delivered'
      },
      closedBy: {
        type: 'string',
        description: 'Person closing the epic'
      }
    },
    required: ['epicId'],
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: CloseEpicInput, context: RequestContext) {
    try {
      const db = context.db;
      const now = Math.floor(Date.now() / 1000);

      // Verify epic exists
      const epicResult = await db.get(
        'SELECT * FROM agile_epics WHERE id = ? AND project_id = ?',
        [input.epicId, context.projectId || 'default']
      );

      if (!epicResult.success || !epicResult.data) {
        return createErrorResult({
          code: 'NOT_FOUND',
          message: 'Epic not found',
          category: 'validation'
        });
      }

      const epic = epicResult.data;

      // Get completion metrics
      const storiesResult = await db.query(
        'SELECT * FROM agile_stories WHERE epic_id = ? AND project_id = ?',
        [input.epicId, context.projectId || 'default']
      );

      const stories = storiesResult.data || [];
      const totalStories = stories.length;
      const completedStories = stories.filter(s => s.status === 'done').length;
      const incompleteStories = stories.filter(s => s.status !== 'done');

      // Check if epic can be closed (at least 80% stories completed)
      const completionRate = totalStories > 0 ? (completedStories / totalStories) * 100 : 100;
      
      if (completionRate < 80) {
        return createErrorResult({
          code: 'VALIDATION_ERROR',
          message: `Epic cannot be closed with only ${Math.round(completionRate)}% completion. At least 80% of stories must be completed.`,
          details: {
            totalStories,
            completedStories,
            completionRate: Math.round(completionRate),
            incompleteStories: incompleteStories.map(s => ({ id: s.id, title: s.title, status: s.status }))
          },
          category: 'validation'
        });
      }

      // Update epic status to closed
      await db.run(
        `UPDATE agile_epics SET 
         status = 'closed', 
         completion_notes = ?, 
         business_value_realized = ?, 
         closed_by = ?, 
         closed_at = ?, 
         updated_at = ?
         WHERE id = ?`,
        [
          input.completionNotes || '',
          input.businessValueRealized || '',
          input.closedBy || 'system',
          now,
          now,
          input.epicId
        ]
      );

      // Calculate final metrics
      const totalStoryPoints = stories.reduce((sum, s) => sum + (s.story_points || 0), 0);
      const completedStoryPoints = stories.filter(s => s.status === 'done')
        .reduce((sum, s) => sum + (s.story_points || 0), 0);

      return createSuccessResult({
        epicId: input.epicId,
        epicTitle: epic.title,
        closedAt: new Date(now * 1000).toISOString(),
        closedBy: input.closedBy || 'system',
        completionMetrics: {
          totalStories,
          completedStories,
          completionRate: Math.round(completionRate),
          totalStoryPoints,
          completedStoryPoints,
          storyPointsCompletionRate: totalStoryPoints > 0 ? 
            Math.round((completedStoryPoints / totalStoryPoints) * 100) : 0
        },
        businessValue: {
          planned: epic.business_value,
          realized: input.businessValueRealized || 'Not specified'
        },
        successCriteria: JSON.parse(epic.success_criteria || '[]'),
        completionNotes: input.completionNotes || null,
        incompleteStories: incompleteStories.length > 0 ? 
          incompleteStories.map(s => ({ id: s.id, title: s.title, status: s.status })) : []
      });

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

/**
 * Get all stories linked to an epic with filtering
 */
const getEpicStoriesTool = createTool<GetEpicStoriesInput, any>({
  name: 'get_epic_stories',
  description: 'List all stories linked to an epic with optional filtering',
  category: 'agile-management',
  inputSchema: {
    type: 'object',
    properties: {
      epicId: {
        type: 'string',
        description: 'Epic ID to get stories for',
        pattern: '^[a-zA-Z0-9-]+$'
      },
      status: {
        type: 'string',
        enum: ['backlog', 'todo', 'in_progress', 'review', 'done', 'blocked'],
        description: 'Filter by story status'
      },
      sprintId: {
        type: 'string',
        description: 'Filter by sprint ID',
        pattern: '^[a-zA-Z0-9-]+$'
      },
      assignee: {
        type: 'string',
        description: 'Filter by assignee'
      }
    },
    required: ['epicId'],
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: GetEpicStoriesInput, context: RequestContext) {
    try {
      const db = context.db;

      // Verify epic exists
      const epicResult = await db.get(
        'SELECT * FROM agile_epics WHERE id = ? AND project_id = ?',
        [input.epicId, context.projectId || 'default']
      );

      if (!epicResult.success || !epicResult.data) {
        return createErrorResult({
          code: 'NOT_FOUND',
          message: 'Epic not found',
          category: 'validation'
        });
      }

      const epic = epicResult.data;

      // Build query with filters
      let sql = 'SELECT * FROM agile_stories WHERE epic_id = ? AND project_id = ?';
      const params: any[] = [input.epicId, context.projectId || 'default'];

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

      if (input.sprintId) {
        sql += ' AND sprint_id = ?';
        params.push(input.sprintId);
      }

      if (input.assignee) {
        sql += ' AND assignee = ?';
        params.push(input.assignee);
      }

      sql += ' ORDER BY priority DESC, created_at ASC';

      const storiesResult = await db.query(sql, params);
      const stories = storiesResult.data || [];

      // Get sprint information for each story
      const storiesWithSprints = await Promise.all(
        stories.map(async (story: any) => {
          if (story.sprint_id) {
            const sprintResult = await db.get(
              'SELECT id, name, status FROM agile_sprints WHERE id = ?',
              [story.sprint_id]
            );
            return {
              ...story,
              sprint: sprintResult.data || null,
              acceptanceCriteria: JSON.parse(story.acceptance_criteria || '[]'),
              tags: JSON.parse(story.tags || '[]'),
              createdAt: new Date(story.created_at * 1000).toISOString(),
              updatedAt: new Date(story.updated_at * 1000).toISOString()
            };
          }
          return {
            ...story,
            sprint: null,
            acceptanceCriteria: JSON.parse(story.acceptance_criteria || '[]'),
            tags: JSON.parse(story.tags || '[]'),
            createdAt: new Date(story.created_at * 1000).toISOString(),
            updatedAt: new Date(story.updated_at * 1000).toISOString()
          };
        })
      );

      // Calculate summary metrics
      const summary = {
        total: stories.length,
        byStatus: {
          todo: stories.filter(s => s.status === 'todo').length,
          in_progress: stories.filter(s => s.status === 'in_progress').length,
          review: stories.filter(s => s.status === 'review').length,
          done: stories.filter(s => s.status === 'done').length,
          blocked: stories.filter(s => s.status === 'blocked').length
        },
        storyPoints: {
          total: stories.reduce((sum, s) => sum + (s.story_points || 0), 0),
          completed: stories.filter(s => s.status === 'done')
            .reduce((sum, s) => sum + (s.story_points || 0), 0)
        }
      };

      return createSuccessResult({
        epicId: input.epicId,
        epicTitle: epic.title,
        epicStatus: epic.status,
        filters: {
          status: input.status || null,
          sprintId: input.sprintId || null,
          assignee: input.assignee || null
        },
        stories: storiesWithSprints,
        summary
      });

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

/**
 * Validate epic requirements and completion readiness
 */
const validateEpicRequirementsTool = createTool<ValidateEpicRequirementsInput, any>({
  name: 'validate_epic_requirements',
  description: 'Validate epic completeness and readiness for closure',
  category: 'agile-management',
  inputSchema: {
    type: 'object',
    properties: {
      epicId: {
        type: 'string',
        description: 'Epic ID to validate',
        pattern: '^[a-zA-Z0-9-]+$'
      }
    },
    required: ['epicId'],
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: ValidateEpicRequirementsInput, context: RequestContext) {
    try {
      const db = context.db;

      // Verify epic exists
      const epicResult = await db.get(
        'SELECT * FROM agile_epics WHERE id = ? AND project_id = ?',
        [input.epicId, context.projectId || 'default']
      );

      if (!epicResult.success || !epicResult.data) {
        return createErrorResult({
          code: 'NOT_FOUND',
          message: 'Epic not found',
          category: 'validation'
        });
      }

      const epic = epicResult.data;
      const validationResults = [];
      let overallValid = true;

      // Check if epic has basic information
      if (!epic.title || epic.title.trim().length === 0) {
        validationResults.push({
          check: 'title',
          passed: false,
          message: 'Epic title is missing or empty'
        });
        overallValid = false;
      } else {
        validationResults.push({
          check: 'title',
          passed: true,
          message: 'Epic has a valid title'
        });
      }

      if (!epic.description || epic.description.trim().length === 0) {
        validationResults.push({
          check: 'description',
          passed: false,
          message: 'Epic description is missing or empty'
        });
        overallValid = false;
      } else {
        validationResults.push({
          check: 'description',
          passed: true,
          message: 'Epic has a description'
        });
      }

      // Check business value
      if (!epic.business_value || epic.business_value.trim().length === 0) {
        validationResults.push({
          check: 'business_value',
          passed: false,
          message: 'Business value is not defined'
        });
        overallValid = false;
      } else {
        validationResults.push({
          check: 'business_value',
          passed: true,
          message: 'Business value is defined'
        });
      }

      // Check success criteria
      const successCriteria = JSON.parse(epic.success_criteria || '[]');
      if (successCriteria.length === 0) {
        validationResults.push({
          check: 'success_criteria',
          passed: false,
          message: 'No success criteria defined'
        });
        overallValid = false;
      } else {
        validationResults.push({
          check: 'success_criteria',
          passed: true,
          message: `${successCriteria.length} success criteria defined`
        });
      }

      // Check if epic has stories
      const storiesResult = await db.query(
        'SELECT * FROM agile_stories WHERE epic_id = ? AND project_id = ?',
        [input.epicId, context.projectId || 'default']
      );

      const stories = storiesResult.data || [];
      if (stories.length === 0) {
        validationResults.push({
          check: 'stories',
          passed: false,
          message: 'Epic has no linked stories'
        });
        overallValid = false;
      } else {
        validationResults.push({
          check: 'stories',
          passed: true,
          message: `Epic has ${stories.length} linked stories`
        });
      }

      // Check story completion rate
      const completedStories = stories.filter(s => s.status === 'done').length;
      const completionRate = stories.length > 0 ? (completedStories / stories.length) * 100 : 0;
      
      if (completionRate < 80) {
        validationResults.push({
          check: 'completion_rate',
          passed: false,
          message: `Only ${Math.round(completionRate)}% of stories completed (minimum 80% required)`
        });
        overallValid = false;
      } else {
        validationResults.push({
          check: 'completion_rate',
          passed: true,
          message: `${Math.round(completionRate)}% of stories completed`
        });
      }

      // Check for blocked stories
      const blockedStories = stories.filter(s => s.status === 'blocked');
      if (blockedStories.length > 0) {
        validationResults.push({
          check: 'blocked_stories',
          passed: false,
          message: `${blockedStories.length} stories are still blocked`
        });
        overallValid = false;
      } else {
        validationResults.push({
          check: 'blocked_stories',
          passed: true,
          message: 'No blocked stories'
        });
      }

      // Check dependencies
      const dependencies = JSON.parse(epic.dependencies || '[]');
      validationResults.push({
        check: 'dependencies',
        passed: true,
        message: dependencies.length > 0 ? 
          `${dependencies.length} dependencies documented` : 
          'No dependencies specified'
      });

      return createSuccessResult({
        epicId: input.epicId,
        epicTitle: epic.title,
        epicStatus: epic.status,
        overallValid,
        validationResults,
        summary: {
          totalChecks: validationResults.length,
          passed: validationResults.filter(r => r.passed).length,
          failed: validationResults.filter(r => !r.passed).length
        },
        recommendations: overallValid ? 
          ['Epic is ready for closure'] : 
          validationResults.filter(r => !r.passed).map(r => `Fix: ${r.message}`)
      });

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

/**
 * Generate comprehensive epic report
 */
const generateEpicReportTool = createTool<GenerateEpicReportInput, any>({
  name: 'generate_epic_report',
  description: 'Generate comprehensive epic report with metrics and timeline',
  category: 'agile-management',
  inputSchema: {
    type: 'object',
    properties: {
      epicId: {
        type: 'string',
        description: 'Epic ID to report on',
        pattern: '^[a-zA-Z0-9-]+$'
      },
      includeMetrics: {
        type: 'boolean',
        description: 'Include detailed metrics in report',
        default: true
      },
      includeTimeline: {
        type: 'boolean',
        description: 'Include timeline analysis in report',
        default: true
      }
    },
    required: ['epicId'],
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: GenerateEpicReportInput, context: RequestContext) {
    try {
      const db = context.db;
      const reportId = randomUUID();
      const now = Math.floor(Date.now() / 1000);

      // Verify epic exists
      const epicResult = await db.get(
        'SELECT * FROM agile_epics WHERE id = ? AND project_id = ?',
        [input.epicId, context.projectId || 'default']
      );

      if (!epicResult.success || !epicResult.data) {
        return createErrorResult({
          code: 'NOT_FOUND',
          message: 'Epic not found',
          category: 'validation'
        });
      }

      const epic = epicResult.data;

      // Get epic stories
      const storiesResult = await db.query(
        'SELECT * FROM agile_stories WHERE epic_id = ? AND project_id = ?',
        [input.epicId, context.projectId || 'default']
      );

      const stories = storiesResult.data || [];

      // Calculate metrics if requested
      let metrics = null;
      if (input.includeMetrics !== false) {
        const totalStories = stories.length;
        const completedStories = stories.filter(s => s.status === 'done').length;
        const totalStoryPoints = stories.reduce((sum, s) => sum + (s.story_points || 0), 0);
        const completedStoryPoints = stories.filter(s => s.status === 'done')
          .reduce((sum, s) => sum + (s.story_points || 0), 0);

        metrics = {
          stories: {
            total: totalStories,
            completed: completedStories,
            inProgress: stories.filter(s => s.status === 'in_progress').length,
            blocked: stories.filter(s => s.status === 'blocked').length,
            completionRate: totalStories > 0 ? Math.round((completedStories / totalStories) * 100) : 0
          },
          storyPoints: {
            total: totalStoryPoints,
            completed: completedStoryPoints,
            completionRate: totalStoryPoints > 0 ? 
              Math.round((completedStoryPoints / totalStoryPoints) * 100) : 0
          },
          velocity: {
            averageStoryPoints: totalStories > 0 ? Math.round(totalStoryPoints / totalStories) : 0,
            completedAveragePoints: completedStories > 0 ? 
              Math.round(completedStoryPoints / completedStories) : 0
          }
        };
      }

      // Get timeline if requested
      let timeline = null;
      if (input.includeTimeline !== false) {
        const timelineResult = await db.query(
          `SELECT 
             s.id as sprint_id,
             s.name as sprint_name,
             s.start_date,
             s.end_date,
             s.status,
             COUNT(st.id) as story_count,
             SUM(CASE WHEN st.status = 'done' THEN 1 ELSE 0 END) as completed_stories
           FROM agile_sprints s
           LEFT JOIN agile_stories st ON s.id = st.sprint_id AND st.epic_id = ?
           WHERE s.project_id = ? AND st.epic_id IS NOT NULL
           GROUP BY s.id, s.name, s.start_date, s.end_date, s.status
           ORDER BY s.start_date ASC`,
          [input.epicId, context.projectId || 'default']
        );

        timeline = timelineResult.data || [];
      }

      // Get dependencies and risks
      const dependencies = JSON.parse(epic.dependencies || '[]');
      const risks = JSON.parse(epic.risks || '[]');
      const successCriteria = JSON.parse(epic.success_criteria || '[]');

      // Create report object
      const report = {
        reportId,
        epicId: input.epicId,
        epicTitle: epic.title,
        epicDescription: epic.description,
        epicStatus: epic.status,
        businessValue: epic.business_value,
        targetQuarter: epic.target_quarter,
        generatedAt: new Date(now * 1000).toISOString(),
        metrics,
        timeline,
        dependencies,
        risks,
        successCriteria,
        summary: {
          status: epic.status,
          readyForClosure: metrics ? metrics.stories.completionRate >= 80 : false,
          keyMetrics: metrics ? {
            totalStories: metrics.stories.total,
            completionRate: metrics.stories.completionRate,
            blockedStories: metrics.stories.blocked
          } : null
        }
      };

      // Store report in database
      await db.run(
        `INSERT INTO agile_sprint_reports 
         (id, project_id, sprint_id, report_type, report_data, generated_by)
         VALUES (?, ?, ?, 'epic_report', ?, 'system')`,
        [reportId, context.projectId || 'default', 'epic-' + input.epicId, JSON.stringify(report)]
      );

      return createSuccessResult(report);

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

/**
 * Generate comprehensive sprint report with metrics and analysis
 */
const generateSprintReportTool = createTool<GenerateSprintReportInput, any>({
  name: 'generate_sprint_report',
  description: 'Generate comprehensive sprint report with velocity, blockers, and completion analysis',
  category: 'agile-management',
  inputSchema: {
    type: 'object',
    properties: {
      sprintId: {
        type: 'string',
        description: 'Sprint ID to report on',
        pattern: '^[a-zA-Z0-9-]+$'
      },
      includeVelocity: {
        type: 'boolean',
        description: 'Include velocity metrics in report',
        default: true
      },
      includeBlockers: {
        type: 'boolean',
        description: 'Include blocker analysis in report',
        default: true
      }
    },
    required: ['sprintId'],
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: GenerateSprintReportInput, context: RequestContext) {
    try {
      const db = context.db;
      const reportId = randomUUID();
      const now = Math.floor(Date.now() / 1000);

      // Get sprint details
      const sprintResult = await db.get(
        'SELECT * FROM agile_sprints WHERE id = ? AND project_id = ?',
        [input.sprintId, context.projectId || 'default']
      );

      if (!sprintResult.success || !sprintResult.data) {
        return createErrorResult({
          code: 'NOT_FOUND',
          message: 'Sprint not found',
          category: 'validation'
        });
      }

      const sprint = sprintResult.data;

      // Get sprint stories
      const storiesResult = await db.query(
        'SELECT * FROM agile_stories WHERE sprint_id = ? AND project_id = ?',
        [input.sprintId, context.projectId || 'default']
      );

      const stories = storiesResult.data || [];

      // Calculate basic metrics
      const totalStories = stories.length;
      const completedStories = stories.filter(s => s.status === 'done').length;
      const totalStoryPoints = stories.reduce((sum, s) => sum + (s.story_points || 0), 0);
      const completedStoryPoints = stories.filter(s => s.status === 'done')
        .reduce((sum, s) => sum + (s.story_points || 0), 0);

      // Calculate velocity if requested
      let velocityMetrics = null;
      if (input.includeVelocity !== false) {
        const plannedVelocity = totalStoryPoints;
        const actualVelocity = completedStoryPoints;
        const velocityRatio = plannedVelocity > 0 ? actualVelocity / plannedVelocity : 0;

        // Store velocity data
        await db.run(
          `INSERT INTO agile_team_velocity 
           (id, project_id, sprint_id, planned_points, completed_points, velocity_ratio)
           VALUES (?, ?, ?, ?, ?, ?)`,
          [randomUUID(), context.projectId || 'default', input.sprintId, 
           plannedVelocity, actualVelocity, velocityRatio]
        );

        velocityMetrics = {
          planned: plannedVelocity,
          actual: actualVelocity,
          ratio: Math.round(velocityRatio * 100) / 100,
          percentage: Math.round(velocityRatio * 100)
        };
      }

      // Analyze blockers if requested
      let blockerAnalysis = null;
      if (input.includeBlockers !== false) {
        const blockersResult = await db.query(
          `SELECT sb.*, s.title as story_title
           FROM agile_story_blockers sb
           JOIN agile_stories s ON sb.blocked_story_id = s.id
           WHERE s.sprint_id = ? AND sb.project_id = ?`,
          [input.sprintId, context.projectId || 'default']
        );

        const blockers = blockersResult.data || [];
        const activeBlockers = blockers.filter(b => b.status === 'active');
        const resolvedBlockers = blockers.filter(b => b.status === 'resolved');

        blockerAnalysis = {
          total: blockers.length,
          active: activeBlockers.length,
          resolved: resolvedBlockers.length,
          byType: {
            dependency: blockers.filter(b => b.blocker_type === 'dependency').length,
            external: blockers.filter(b => b.blocker_type === 'external').length,
            technical: blockers.filter(b => b.blocker_type === 'technical').length,
            resource: blockers.filter(b => b.blocker_type === 'resource').length
          },
          details: blockers.map(b => ({
            id: b.id,
            storyTitle: b.story_title,
            type: b.blocker_type,
            reason: b.reason,
            status: b.status
          }))
        };
      }

      // Get sprint completion data
      const completionResult = await db.get(
        'SELECT * FROM agile_sprint_completions WHERE sprint_id = ? AND project_id = ?',
        [input.sprintId, context.projectId || 'default']
      );

      const completion = completionResult.data;

      // Create comprehensive report
      const report = {
        reportId,
        sprintId: input.sprintId,
        sprintName: sprint.name,
        sprintGoal: sprint.goal,
        sprintStatus: sprint.status,
        sprintDuration: sprint.duration,
        startDate: sprint.start_date ? new Date(sprint.start_date * 1000).toISOString() : null,
        endDate: sprint.end_date ? new Date(sprint.end_date * 1000).toISOString() : null,
        generatedAt: new Date(now * 1000).toISOString(),
        
        storyMetrics: {
          total: totalStories,
          completed: completedStories,
          inProgress: stories.filter(s => s.status === 'in_progress').length,
          blocked: stories.filter(s => s.status === 'blocked').length,
          completionRate: totalStories > 0 ? Math.round((completedStories / totalStories) * 100) : 0
        },

        storyPointMetrics: {
          total: totalStoryPoints,
          completed: completedStoryPoints,
          completionRate: totalStoryPoints > 0 ? 
            Math.round((completedStoryPoints / totalStoryPoints) * 100) : 0
        },

        velocityMetrics,
        blockerAnalysis,

        completion: completion ? {
          carryoverStories: JSON.parse(completion.carryover_stories || '[]'),
          completionNotes: completion.completion_notes,
          completedBy: completion.completed_by,
          completedAt: completion.completed_at ? 
            new Date(completion.completed_at * 1000).toISOString() : null
        } : null,

        teamPerformance: {
          storiesPerDay: sprint.duration && totalStories ? 
            Math.round((totalStories / sprint.duration) * 10) / 10 : 0,
          pointsPerDay: sprint.duration && totalStoryPoints ? 
            Math.round((totalStoryPoints / sprint.duration) * 10) / 10 : 0
        }
      };

      // Store report
      await db.run(
        `INSERT INTO agile_sprint_reports 
         (id, project_id, sprint_id, report_type, report_data, generated_by)
         VALUES (?, ?, ?, 'sprint_report', ?, 'system')`,
        [reportId, context.projectId || 'default', input.sprintId, JSON.stringify(report)]
      );

      return createSuccessResult(report);

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

/**
 * Get team velocity trends over multiple sprints
 */
const getTeamVelocityTrendTool = createTool<GetTeamVelocityTrendInput, any>({
  name: 'get_team_velocity_trend',
  description: 'Analyze team velocity trends over multiple sprints',
  category: 'agile-management',
  inputSchema: {
    type: 'object',
    properties: {
      teamId: {
        type: 'string',
        description: 'Team ID to analyze (optional)'
      },
      numberOfSprints: {
        type: 'integer',
        description: 'Number of recent sprints to analyze',
        minimum: 1,
        maximum: 20,
        default: 6
      },
      startDate: {
        type: 'string',
        description: 'Start date for analysis (ISO format)'
      },
      endDate: {
        type: 'string',
        description: 'End date for analysis (ISO format)'
      }
    },
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: GetTeamVelocityTrendInput, context: RequestContext) {
    try {
      const db = context.db;

      // Build query for sprints
      let sql = `
        SELECT s.*, 
               COUNT(st.id) as total_stories,
               SUM(st.story_points) as total_points,
               SUM(CASE WHEN st.status = 'done' THEN st.story_points ELSE 0 END) as completed_points
        FROM agile_sprints s
        LEFT JOIN agile_stories st ON s.id = st.sprint_id
        WHERE s.project_id = ? AND s.status = 'completed'
      `;
      const params: any[] = [context.projectId || 'default'];

      if (input.startDate) {
        sql += ' AND s.start_date >= ?';
        params.push(Math.floor(new Date(input.startDate).getTime() / 1000));
      }

      if (input.endDate) {
        sql += ' AND s.end_date <= ?';
        params.push(Math.floor(new Date(input.endDate).getTime() / 1000));
      }

      sql += ' GROUP BY s.id ORDER BY s.start_date DESC';

      if (input.numberOfSprints) {
        sql += ' LIMIT ?';
        params.push(input.numberOfSprints);
      }

      const sprintsResult = await db.query(sql, params);
      const sprints = (sprintsResult.data || []).reverse(); // Reverse to get chronological order

      if (sprints.length === 0) {
        return createSuccessResult({
          message: 'No completed sprints found for analysis',
          velocityTrend: [],
          summary: null
        });
      }

      // Calculate velocity trend
      const velocityTrend = sprints.map((sprint: any) => ({
        sprintId: sprint.id,
        sprintName: sprint.name,
        startDate: sprint.start_date ? new Date(sprint.start_date * 1000).toISOString() : null,
        endDate: sprint.end_date ? new Date(sprint.end_date * 1000).toISOString() : null,
        plannedPoints: sprint.total_points || 0,
        completedPoints: sprint.completed_points || 0,
        velocityRatio: sprint.total_points > 0 ? 
          Math.round((sprint.completed_points / sprint.total_points) * 100) / 100 : 0,
        totalStories: sprint.total_stories || 0
      }));

      // Calculate trend analysis
      const velocities = velocityTrend.map(v => v.completedPoints);
      const averageVelocity = velocities.length > 0 ? 
        Math.round(velocities.reduce((sum, v) => sum + v, 0) / velocities.length) : 0;

      const recentVelocities = velocities.slice(-3); // Last 3 sprints
      const olderVelocities = velocities.slice(0, -3);
      
      const recentAverage = recentVelocities.length > 0 ? 
        Math.round(recentVelocities.reduce((sum, v) => sum + v, 0) / recentVelocities.length) : 0;
      const olderAverage = olderVelocities.length > 0 ? 
        Math.round(olderVelocities.reduce((sum, v) => sum + v, 0) / olderVelocities.length) : 0;

      const trend = olderAverage > 0 ? ((recentAverage - olderAverage) / olderAverage) * 100 : 0;

      const summary = {
        totalSprints: sprints.length,
        averageVelocity,
        recentAverageVelocity: recentAverage,
        velocityTrend: Math.round(trend * 100) / 100,
        trendDirection: trend > 5 ? 'improving' : trend < -5 ? 'declining' : 'stable',
        highestVelocity: Math.max(...velocities),
        lowestVelocity: Math.min(...velocities),
        consistency: {
          standardDeviation: Math.round(calculateStandardDeviation(velocities) * 100) / 100,
          coefficient: averageVelocity > 0 ? 
            Math.round((calculateStandardDeviation(velocities) / averageVelocity) * 100) / 100 : 0
        }
      };

      return createSuccessResult({
        teamId: input.teamId || null,
        analysisRange: {
          startDate: sprints[0]?.start_date ? new Date(sprints[0].start_date * 1000).toISOString() : null,
          endDate: sprints[sprints.length - 1]?.end_date ? 
            new Date(sprints[sprints.length - 1].end_date * 1000).toISOString() : null,
          sprintCount: sprints.length
        },
        velocityTrend,
        summary
      });

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

// Helper function for standard deviation calculation
function calculateStandardDeviation(values: number[]): number {
  if (values.length === 0) return 0;
  const mean = values.reduce((sum, val) => sum + val, 0) / values.length;
  const squaredDiffs = values.map(val => Math.pow(val - mean, 2));
  const avgSquaredDiff = squaredDiffs.reduce((sum, val) => sum + val, 0) / values.length;
  return Math.sqrt(avgSquaredDiff);
}

/**
 * Get cycle time metrics for stories
 */
const getCycleTimeMetricsTool = createTool<GetCycleTimeMetricsInput, any>({
  name: 'get_cycle_time_metrics',
  description: 'Analyze story cycle time metrics and bottlenecks',
  category: 'agile-management',
  inputSchema: {
    type: 'object',
    properties: {
      sprintId: {
        type: 'string',
        description: 'Sprint ID to analyze',
        pattern: '^[a-zA-Z0-9-]+$'
      },
      storyIds: {
        type: 'array',
        items: { type: 'string' },
        description: 'Specific story IDs to analyze'
      },
      dateRange: {
        type: 'object',
        properties: {
          start: { type: 'string', description: 'Start date (ISO format)' },
          end: { type: 'string', description: 'End date (ISO format)' }
        }
      }
    },
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: GetCycleTimeMetricsInput, context: RequestContext) {
    try {
      const db = context.db;

      // Build query
      let sql = `
        SELECT s.id, s.title, s.status, s.created_at, s.updated_at, s.story_points,
               sl.from_status, sl.to_status, sl.changed_at, sl.duration_in_status
        FROM agile_stories s
        LEFT JOIN agile_story_lifecycle sl ON s.id = sl.story_id
        WHERE s.project_id = ?
      `;
      const params: any[] = [context.projectId || 'default'];

      if (input.sprintId) {
        sql += ' AND s.sprint_id = ?';
        params.push(input.sprintId);
      }

      if (input.storyIds && input.storyIds.length > 0) {
        sql += ` AND s.id IN (${input.storyIds.map(() => '?').join(',')})`;
        params.push(...input.storyIds);
      }

      if (input.dateRange) {
        if (input.dateRange.start) {
          sql += ' AND s.created_at >= ?';
          params.push(Math.floor(new Date(input.dateRange.start).getTime() / 1000));
        }
        if (input.dateRange.end) {
          sql += ' AND s.created_at <= ?';
          params.push(Math.floor(new Date(input.dateRange.end).getTime() / 1000));
        }
      }

      sql += ' ORDER BY s.created_at ASC';

      const result = await db.query(sql, params);
      const data = result.data || [];

      // Group by story and calculate metrics
      const storyMetrics = new Map();
      
      data.forEach((row: any) => {
        if (!storyMetrics.has(row.id)) {
          storyMetrics.set(row.id, {
            id: row.id,
            title: row.title,
            storyPoints: row.story_points || 0,
            totalCycleTime: row.updated_at - row.created_at,
            statusDurations: new Map(),
            currentStatus: row.status
          });
        }

        if (row.duration_in_status && row.from_status) {
          const story = storyMetrics.get(row.id);
          story.statusDurations.set(row.from_status, row.duration_in_status);
        }
      });

      const stories = Array.from(storyMetrics.values()).map(story => ({
        ...story,
        statusDurations: Object.fromEntries(story.statusDurations),
        cycleTimeDays: Math.round((story.totalCycleTime / (24 * 60 * 60)) * 10) / 10
      }));

      // Calculate summary metrics
      const completedStories = stories.filter(s => s.currentStatus === 'done');
      const avgCycleTime = completedStories.length > 0 ? 
        completedStories.reduce((sum, s) => sum + s.cycleTimeDays, 0) / completedStories.length : 0;

      return createSuccessResult({
        stories,
        summary: {
          totalStories: stories.length,
          completedStories: completedStories.length,
          averageCycleTimeDays: Math.round(avgCycleTime * 10) / 10,
          bottlenecks: {
            todo: Math.round((stories.reduce((sum, s) => sum + (s.statusDurations.todo || 0), 0) / stories.length) / (24 * 60 * 60) * 10) / 10,
            in_progress: Math.round((stories.reduce((sum, s) => sum + (s.statusDurations.in_progress || 0), 0) / stories.length) / (24 * 60 * 60) * 10) / 10,
            review: Math.round((stories.reduce((sum, s) => sum + (s.statusDurations.review || 0), 0) / stories.length) / (24 * 60 * 60) * 10) / 10
          }
        }
      });

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

/**
 * Generate retrospective template with metrics
 */
const generateRetrospectiveTemplateTool = createTool<GenerateRetrospectiveTemplateInput, any>({
  name: 'generate_retrospective_template',
  description: 'Generate structured retrospective template with sprint metrics',
  category: 'agile-management',
  inputSchema: {
    type: 'object',
    properties: {
      sprintId: {
        type: 'string',
        description: 'Sprint ID for retrospective',
        pattern: '^[a-zA-Z0-9-]+$'
      },
      includeMetrics: {
        type: 'boolean',
        description: 'Include sprint metrics',
        default: true
      },
      templateType: {
        type: 'string',
        enum: ['standard', 'starfish', 'sailboat'],
        description: 'Retrospective template type',
        default: 'standard'
      }
    },
    required: ['sprintId'],
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: GenerateRetrospectiveTemplateInput, context: RequestContext) {
    try {
      const db = context.db;

      // Get sprint data
      const sprintResult = await db.get(
        'SELECT * FROM agile_sprints WHERE id = ? AND project_id = ?',
        [input.sprintId, context.projectId || 'default']
      );

      if (!sprintResult.success || !sprintResult.data) {
        return createErrorResult({
          code: 'NOT_FOUND',
          message: 'Sprint not found',
          category: 'validation'
        });
      }

      const sprint = sprintResult.data;

      // Get metrics if requested
      let metrics = null;
      if (input.includeMetrics !== false) {
        const storiesResult = await db.query(
          'SELECT * FROM agile_stories WHERE sprint_id = ? AND project_id = ?',
          [input.sprintId, context.projectId || 'default']
        );

        const stories = storiesResult.data || [];
        metrics = {
          totalStories: stories.length,
          completedStories: stories.filter(s => s.status === 'done').length,
          totalStoryPoints: stories.reduce((sum, s) => sum + (s.story_points || 0), 0),
          completedStoryPoints: stories.filter(s => s.status === 'done')
            .reduce((sum, s) => sum + (s.story_points || 0), 0)
        };
      }

      // Generate template based on type
      const templates = {
        standard: {
          title: `Sprint ${sprint.name} Retrospective`,
          sections: [
            {
              title: "What went well?",
              description: "Positive aspects and successes from this sprint",
              items: ["List achievements and positive outcomes"]
            },
            {
              title: "What could be improved?",
              description: "Areas for improvement and challenges faced",
              items: ["Identify bottlenecks and pain points"]
            },
            {
              title: "Action items",
              description: "Concrete steps to improve next sprint",
              items: ["Define specific, actionable improvements"]
            }
          ]
        },
        starfish: {
          title: `Sprint ${sprint.name} Starfish Retrospective`,
          sections: [
            { title: "Keep Doing", description: "Continue these practices", items: [] },
            { title: "More Of", description: "Increase these activities", items: [] },
            { title: "Start Doing", description: "Begin new practices", items: [] },
            { title: "Less Of", description: "Reduce these activities", items: [] },
            { title: "Stop Doing", description: "Eliminate these practices", items: [] }
          ]
        },
        sailboat: {
          title: `Sprint ${sprint.name} Sailboat Retrospective`,
          sections: [
            { title: "Wind (What helped us?)", description: "Factors that accelerated progress", items: [] },
            { title: "Anchors (What slowed us down?)", description: "Impediments and blockers", items: [] },
            { title: "Rocks (What risks do we see?)", description: "Potential future obstacles", items: [] },
            { title: "Island (Where are we going?)", description: "Sprint and project goals", items: [] }
          ]
        }
      };

      const template = templates[input.templateType || 'standard'];

      return createSuccessResult({
        sprintId: input.sprintId,
        sprintName: sprint.name,
        templateType: input.templateType || 'standard',
        generatedAt: new Date().toISOString(),
        template,
        metrics
      });

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

/**
 * Generate epic burndown chart data
 */
const generateEpicBurndownTool = createTool<GenerateEpicBurndownInput, any>({
  name: 'generate_epic_burndown',
  description: 'Generate epic burndown chart data with story completion over time',
  category: 'agile-management',
  inputSchema: {
    type: 'object',
    properties: {
      epicId: {
        type: 'string',
        description: 'Epic ID for burndown chart',
        pattern: '^[a-zA-Z0-9-]+$'
      },
      granularity: {
        type: 'string',
        enum: ['daily', 'weekly', 'sprint'],
        description: 'Data point granularity',
        default: 'weekly'
      }
    },
    required: ['epicId'],
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: GenerateEpicBurndownInput, context: RequestContext) {
    try {
      const db = context.db;

      // Get epic and validate
      const epicResult = await db.get(
        'SELECT * FROM agile_epics WHERE id = ? AND project_id = ?',
        [input.epicId, context.projectId || 'default']
      );

      if (!epicResult.success || !epicResult.data) {
        return createErrorResult({
          code: 'NOT_FOUND',
          message: 'Epic not found',
          category: 'validation'
        });
      }

      const epic = epicResult.data;

      // Get epic metrics over time
      const metricsResult = await db.query(
        `SELECT * FROM agile_epic_metrics 
         WHERE epic_id = ? AND project_id = ? 
         ORDER BY calculation_date ASC`,
        [input.epicId, context.projectId || 'default']
      );

      let dataPoints = metricsResult.data || [];

      // If no historical data, create current snapshot
      if (dataPoints.length === 0) {
        const storiesResult = await db.query(
          'SELECT * FROM agile_stories WHERE epic_id = ? AND project_id = ?',
          [input.epicId, context.projectId || 'default']
        );

        const stories = storiesResult.data || [];
        const now = Math.floor(Date.now() / 1000);

        dataPoints = [{
          calculation_date: now,
          stories_total: stories.length,
          stories_completed: stories.filter(s => s.status === 'done').length,
          story_points_total: stories.reduce((sum, s) => sum + (s.story_points || 0), 0),
          story_points_completed: stories.filter(s => s.status === 'done')
            .reduce((sum, s) => sum + (s.story_points || 0), 0),
          completion_percentage: stories.length > 0 ? 
            (stories.filter(s => s.status === 'done').length / stories.length) * 100 : 0
        }];
      }

      // Format data for chart
      const chartData = dataPoints.map((point: any) => ({
        date: new Date(point.calculation_date * 1000).toISOString(),
        totalStories: point.stories_total,
        completedStories: point.stories_completed,
        remainingStories: point.stories_total - point.stories_completed,
        totalPoints: point.story_points_total,
        completedPoints: point.story_points_completed,
        remainingPoints: point.story_points_total - point.story_points_completed,
        completionPercentage: point.completion_percentage
      }));

      return createSuccessResult({
        epicId: input.epicId,
        epicTitle: epic.title,
        granularity: input.granularity || 'weekly',
        chartData,
        summary: {
          totalDataPoints: chartData.length,
          currentProgress: chartData[chartData.length - 1]?.completionPercentage || 0,
          estimatedCompletion: estimateEpicCompletion(chartData)
        }
      });

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

// Helper function for epic completion estimation
function estimateEpicCompletion(chartData: any[]): string | null {
    if (chartData.length < 2) return null;
    
    const recent = chartData.slice(-3);
    const avgProgress = recent.reduce((sum, point, idx, arr) => {
      if (idx === 0) return 0;
      return sum + (point.completionPercentage - arr[idx - 1].completionPercentage);
    }, 0) / (recent.length - 1);

    if (avgProgress <= 0) return null;

    const currentProgress = chartData[chartData.length - 1].completionPercentage;
    const remainingProgress = 100 - currentProgress;
    const daysToComplete = Math.ceil(remainingProgress / avgProgress);
    
    const estimatedDate = new Date();
    estimatedDate.setDate(estimatedDate.getDate() + daysToComplete);
    
    return estimatedDate.toISOString();
}

/**
 * Get cross-sprint analytics for trends and patterns
 */
const getCrossSprintAnalyticsTool = createTool<GetCrossSprintAnalyticsInput, any>({
  name: 'get_cross_sprint_analytics',
  description: 'Analyze patterns and trends across multiple sprints',
  category: 'agile-management',
  inputSchema: {
    type: 'object',
    properties: {
      numberOfSprints: {
        type: 'integer',
        description: 'Number of recent sprints to analyze',
        minimum: 2,
        maximum: 20,
        default: 6
      },
      teamId: {
        type: 'string',
        description: 'Team ID for analysis (optional)'
      },
      epicId: {
        type: 'string',
        description: 'Epic ID for analysis (optional)',
        pattern: '^[a-zA-Z0-9-]+$'
      }
    },
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: GetCrossSprintAnalyticsInput, context: RequestContext) {
    try {
      const db = context.db;

      // Get recent sprints
      let sql = `
        SELECT s.*, 
               COUNT(st.id) as total_stories,
               SUM(st.story_points) as total_points,
               SUM(CASE WHEN st.status = 'done' THEN st.story_points ELSE 0 END) as completed_points,
               SUM(CASE WHEN st.status = 'done' THEN 1 ELSE 0 END) as completed_stories
        FROM agile_sprints s
        LEFT JOIN agile_stories st ON s.id = st.sprint_id
        WHERE s.project_id = ? AND s.status = 'completed'
      `;
      const params: any[] = [context.projectId || 'default'];

      if (input.epicId) {
        sql += ' AND st.epic_id = ?';
        params.push(input.epicId);
      }

      sql += ' GROUP BY s.id ORDER BY s.start_date DESC LIMIT ?';
      params.push(input.numberOfSprints || 6);

      const sprintsResult = await db.query(sql, params);
      const sprints = (sprintsResult.data || []).reverse();

      if (sprints.length === 0) {
        return createSuccessResult({
          message: 'No completed sprints found',
          analytics: null
        });
      }

      // Calculate analytics
      const velocities = sprints.map(s => s.completed_points || 0);
      const storyCompletions = sprints.map(s => s.completed_stories || 0);
      
      const analytics = {
        sprintCount: sprints.length,
        velocityTrend: {
          average: Math.round(velocities.reduce((sum, v) => sum + v, 0) / velocities.length),
          trend: velocities.length > 1 ? 
            ((velocities[velocities.length - 1] - velocities[0]) / velocities[0]) * 100 : 0,
          consistency: calculateStandardDeviation(velocities)
        },
        storyTrend: {
          average: Math.round(storyCompletions.reduce((sum, v) => sum + v, 0) / storyCompletions.length),
          trend: storyCompletions.length > 1 ?
            ((storyCompletions[storyCompletions.length - 1] - storyCompletions[0]) / storyCompletions[0]) * 100 : 0
        },
        predictability: {
          velocityVariation: calculateStandardDeviation(velocities),
          storyVariation: calculateStandardDeviation(storyCompletions)
        }
      };

      return createSuccessResult({
        teamId: input.teamId || null,
        epicId: input.epicId || null,
        analysisRange: sprints.length,
        sprints: sprints.map(s => ({
          id: s.id,
          name: s.name,
          completedPoints: s.completed_points || 0,
          completedStories: s.completed_stories || 0,
          startDate: s.start_date ? new Date(s.start_date * 1000).toISOString() : null
        })),
        analytics
      });

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

/**
 * Get dependency report for blockers and relationships
 */
const getDependencyReportTool = createTool<GetDependencyReportInput, any>({
  name: 'get_dependency_report',
  description: 'Generate dependency report showing story blockers and relationships',
  category: 'agile-management',
  inputSchema: {
    type: 'object',
    properties: {
      sprintId: {
        type: 'string',
        description: 'Sprint ID for dependency analysis',
        pattern: '^[a-zA-Z0-9-]+$'
      },
      epicId: {
        type: 'string',
        description: 'Epic ID for dependency analysis',
        pattern: '^[a-zA-Z0-9-]+$'
      },
      includeResolved: {
        type: 'boolean',
        description: 'Include resolved blockers in report',
        default: false
      }
    },
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: GetDependencyReportInput, context: RequestContext) {
    try {
      const db = context.db;

      // Build query for dependencies
      let sql = `
        SELECT 
          sb.id as blocker_id,
          sb.blocker_type,
          sb.reason,
          sb.status as blocker_status,
          sb.resolution,
          sb.resolved_by,
          sb.resolved_at,
          s1.id as blocked_story_id,
          s1.title as blocked_story_title,
          s1.status as blocked_story_status,
          s2.id as blocker_story_id,
          s2.title as blocker_story_title,
          s2.status as blocker_story_status
        FROM agile_story_blockers sb
        JOIN agile_stories s1 ON sb.blocked_story_id = s1.id
        LEFT JOIN agile_stories s2 ON sb.blocker_story_id = s2.id
        WHERE sb.project_id = ?
      `;
      const params: any[] = [context.projectId || 'default'];

      if (input.sprintId) {
        sql += ' AND s1.sprint_id = ?';
        params.push(input.sprintId);
      }

      if (input.epicId) {
        sql += ' AND s1.epic_id = ?';
        params.push(input.epicId);
      }

      if (!input.includeResolved) {
        sql += ' AND sb.status = "active"';
      }

      sql += ' ORDER BY sb.created_at DESC';

      const result = await db.query(sql, params);
      const dependencies = result.data || [];

      // Analyze dependency patterns
      const analysis = {
        total: dependencies.length,
        active: dependencies.filter(d => d.blocker_status === 'active').length,
        resolved: dependencies.filter(d => d.blocker_status === 'resolved').length,
        byType: {
          dependency: dependencies.filter(d => d.blocker_type === 'dependency').length,
          external: dependencies.filter(d => d.blocker_type === 'external').length,
          technical: dependencies.filter(d => d.blocker_type === 'technical').length,
          resource: dependencies.filter(d => d.blocker_type === 'resource').length
        },
        criticalPath: dependencies.filter(d => 
          d.blocker_status === 'active' && 
          d.blocked_story_status !== 'done'
        )
      };

      return createSuccessResult({
        sprintId: input.sprintId || null,
        epicId: input.epicId || null,
        includeResolved: input.includeResolved || false,
        dependencies: dependencies.map(d => ({
          blockerId: d.blocker_id,
          type: d.blocker_type,
          reason: d.reason,
          status: d.blocker_status,
          resolution: d.resolution,
          blockedStory: {
            id: d.blocked_story_id,
            title: d.blocked_story_title,
            status: d.blocked_story_status
          },
          blockerStory: d.blocker_story_id ? {
            id: d.blocker_story_id,
            title: d.blocker_story_title,
            status: d.blocker_story_status
          } : null,
          resolvedBy: d.resolved_by,
          resolvedAt: d.resolved_at ? new Date(d.resolved_at * 1000).toISOString() : null
        })),
        analysis
      });

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

/**
 * Helper tool to move stories to planning status
 * DEPRECATED: Planning status has been removed from the workflow
 */
/* const moveStoryToPlanningTool = createTool<{ storyId: string; notes?: string }, any>({
  name: 'move_story_to_planning',
  description: 'Move a story to planning status (helper for workflow)',
  category: 'agile-management',
  inputSchema: {
    type: 'object',
    properties: {
      storyId: {
        type: 'string',
        description: 'Story ID to move to planning',
        pattern: '^[a-zA-Z0-9-]+$'
      },
      notes: {
        type: 'string',
        description: 'Optional notes about moving to planning',
        maxLength: 1000
      }
    },
    required: ['storyId'],
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: { storyId: string; notes?: string }, context: RequestContext) {
    try {
      // Use the update_story_status tool to move to planning
      const result = await updateStoryStatusTool.execute({
        storyId: input.storyId,
        status: 'planning',
        notes: input.notes || 'Moved to planning for proper workflow'
      }, context);

      if (!result.success) {
        return result;
      }

      return createSuccessResult({
        message: `Story moved to planning status and ready for development`,
        storyId: input.storyId,
        status: 'planning',
        nextSteps: [
          'Review and update story requirements',
          'Ensure acceptance criteria are clear',
          'Estimate story points if needed',
          'Move to "in_progress" when ready to start work'
        ]
      });

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

/**
 * Setup agile management tools
 */
export async function setupAgileManagementTools(): Promise<ToolRegistration> {
  return {
    module: 'agile-management',
    tools: [
      createSprintTool,
      createStoryTool,
      addStoryToSprintTool,
      updateStoryStatusTool,
      getSprintStatsTool,
      listStoriesTool,
      listStoriesWithDetailsTool,
      createEpicTool,
      getEpicTool,
      listEpicsTool,
      listSprintsTool,
      getSprintTool,
      listBacklogTool,
      updateEpicTool,
      updateSprintTool,
      updateStoryTool,
      getStoryTool,
      assignStoryToSprintTool,
      recordStandupTool,
      generateBurndownTool,
      generateVelocityTool,
      getAgileEpicTool,
      getAgileSprintTool,
      // Phase 1 New Tools
      completeSprintTool,
      blockStoryTool,
      unblockStoryTool,
      assignStoryTool,
      addStoryCommentTool,
      splitStoryTool,
      linkStoryToEpicTool,
      // Phase 2 Epic Management Tools
      getEpicProgressTool,
      getEpicTimelineTool,
      closeEpicTool,
      getEpicStoriesTool,
      validateEpicRequirementsTool,
      generateEpicReportTool,
      // Phase 2 Advanced Reporting Tools
      generateSprintReportTool,
      getTeamVelocityTrendTool,
      getCycleTimeMetricsTool,
      generateRetrospectiveTemplateTool,
      generateEpicBurndownTool,
      getCrossSprintAnalyticsTool,
      getDependencyReportTool
      // Workflow Helper Tools removed - planning status no longer used
    ]
  };
}