import { describe, it, expect, beforeEach, vi } from 'vitest';
import { setupAgileManagementTools } from '../tools.js';
import { SqliteManager } from '../../../storage/sqlite-manager.js';

// Mock SqliteManager
vi.mock('../../../storage/sqlite-manager.js', () => ({
  SqliteManager: {
    getInstance: vi.fn()
  }
}));

describe('Agile Management Phase 2 Tools', () => {
  let tools: any;
  let mockDb: vi.Mocked<SqliteManager>;

  beforeEach(async () => {
    vi.clearAllMocks();
    
    // Mock database operations
    mockDb = {
      run: vi.fn().mockResolvedValue({ success: true, changes: 1 }),
      get: vi.fn().mockResolvedValue({ success: true, data: null }),
      query: vi.fn().mockResolvedValue({ success: true, data: [] }),
      transaction: vi.fn().mockImplementation(async (callback) => {
        return await callback(mockDb);
      })
    } as any;
    
    vi.mocked(SqliteManager.getInstance).mockReturnValue(mockDb);
    
    // Setup tools
    const toolRegistration = await setupAgileManagementTools();
    tools = {};
    for (const tool of toolRegistration.tools) {
      tools[tool.name] = tool;
    }
  });

  const createContext = (toolName: string) => ({
    toolName,
    requestId: 'test-req-1',
    projectId: 'test-project',
    userId: 'test-user',
    timestamp: Date.now(),
    db: mockDb
  });

  describe('Epic Management Tools', () => {
    describe('get_epic_progress', () => {
      it('should calculate epic progress based on story completion', async () => {
        // Mock epic data
        mockDb.get.mockResolvedValueOnce({
          success: true,
          data: {
            id: 'epic-123',
            title: 'User Authentication Epic',
            status: 'active',
            story_points: 21
          }
        });

        // Mock stories linked to epic
        mockDb.query.mockResolvedValueOnce({
          success: true,
          data: [
            { id: 'story-1', title: 'Login', story_points: 5, status: 'done' },
            { id: 'story-2', title: 'Registration', story_points: 8, status: 'done' },
            { id: 'story-3', title: 'Password Reset', story_points: 3, status: 'in_progress' },
            { id: 'story-4', title: 'OAuth Integration', story_points: 5, status: 'todo' }
          ]
        });

        const result = await tools.get_epic_progress.execute(
          { epicId: 'epic-123' },
          createContext('get_epic_progress')
        );

        expect(result.success).toBe(true);
        expect(result.data).toMatchObject({
          epicId: 'epic-123',
          epicTitle: 'User Authentication Epic',
          progress: {
            completionPercentage: 50,
            completedStories: 2,
            totalStories: 4,
            inProgressStories: 1,
            blockedStories: 0
          },
          storyPoints: {
            total: 21,
            completed: 13,
            completionPercentage: 62
          }
        });
      });

      it('should handle epic with no stories', async () => {
        mockDb.get.mockResolvedValueOnce({
          success: true,
          data: {
            id: 'epic-empty',
            title: 'Empty Epic',
            status: 'planning'
          }
        });

        mockDb.query.mockResolvedValueOnce({
          success: true,
          data: []
        });

        const result = await tools.get_epic_progress.execute(
          { epicId: 'epic-empty' },
          createContext('get_epic_progress')
        );

        expect(result.success).toBe(true);
        expect(result.data).toMatchObject({
          epicId: 'epic-empty',
          progress: {
            completionPercentage: 0,
            completedStories: 0,
            totalStories: 0
          },
          storyPoints: {
            total: 0,
            completed: 0,
            completionPercentage: 0
          }
        });
      });

      it('should handle non-existent epic', async () => {
        mockDb.get.mockResolvedValueOnce({
          success: true,
          data: null
        });

        const result = await tools.get_epic_progress.execute(
          { epicId: 'non-existent' },
          createContext('get_epic_progress')
        );

        expect(result.success).toBe(false);
        expect(result.error).toBeDefined();
        expect(result.error.message).toContain('Epic not found');
      });
    });

    describe('get_epic_timeline', () => {
      it('should generate timeline with sprint breakdown', async () => {
        mockDb.get.mockResolvedValueOnce({
          success: true,
          data: {
            id: 'epic-123',
            title: 'Feature Epic',
            status: 'active'
          }
        });

        // Mock empty stories query then timeline data
        mockDb.query
          .mockResolvedValueOnce({ success: true, data: [] }) // Stories query
          .mockResolvedValueOnce({ // Timeline query
            success: true,
            data: [
              { 
                sprint_id: 'sprint-1',
                sprint_name: 'Sprint 1',
                start_date: new Date('2025-01-01').getTime() / 1000,
                end_date: new Date('2025-01-14').getTime() / 1000,
                sprint_status: 'completed',
                total_stories: 1,
                completed_stories: 1,
                total_points: 5,
                completed_points: 5
              },
              { 
                sprint_id: 'sprint-2',
                sprint_name: 'Sprint 2',
                start_date: new Date('2025-01-15').getTime() / 1000,
                end_date: new Date('2025-01-28').getTime() / 1000,
                sprint_status: 'active',
                total_stories: 1,
                completed_stories: 0,
                total_points: 8,
                completed_points: 0
              }
            ]
          });

        const result = await tools.get_epic_timeline.execute(
          { epicId: 'epic-123' },
          createContext('get_epic_timeline')
        );

        expect(result.success).toBe(true);
        expect(result.data).toMatchObject({
          epicId: 'epic-123',
          epicTitle: 'Feature Epic',
          timeline: expect.any(Array),
          summary: expect.objectContaining({
            totalSprints: expect.any(Number)
          })
        });
        
        // Since timeline depends on sprints having stories for this epic,
        // and we didn't mock that relationship, timeline will be empty
        expect(result.data.timeline).toEqual([]);
      });
    });

    describe('close_epic', () => {
      it('should close epic with validation', async () => {
        mockDb.get.mockResolvedValueOnce({
          success: true,
          data: {
            id: 'epic-123',
            title: 'Completed Epic',
            status: 'active'
          }
        });

        // Mock stories for sprint then stories for epic
        mockDb.query
          .mockResolvedValueOnce({ // Stories for sprint
            success: true,
            data: [
              { title: 'User login', story_points: 5, status: 'done' },
              { title: 'User profile', story_points: 8, status: 'done' }
            ]
          })
          .mockResolvedValueOnce({ // Blockers query 
            success: true,
            data: [
              { story_title: 'Password reset', reason: 'API dependencies' }
            ]
          });

        const result = await tools.close_epic.execute(
          {
            epicId: 'epic-123',
            completionNotes: 'All features implemented successfully',
            businessValueRealized: 'Improved user onboarding by 40%',
            closedBy: 'john.doe@example.com'
          },
          createContext('close_epic')
        );

        expect(result.success).toBe(true);
        expect(result.data).toMatchObject({
          epicId: 'epic-123',
          epicTitle: 'Completed Epic',
          closedBy: 'john.doe@example.com',
          completionMetrics: expect.objectContaining({
            totalStories: 2,
            completedStories: 2,
            completionRate: 100
          })
        });

        // Verify update was called
        expect(mockDb.run).toHaveBeenCalled();
      });

      it('should fail to close epic with incomplete stories', async () => {
        mockDb.get.mockResolvedValueOnce({
          success: true,
          data: {
            id: 'epic-123',
            title: 'Incomplete Epic',
            status: 'active'
          }
        });

        // Mock stories with some incomplete
        mockDb.query.mockResolvedValueOnce({
          success: true,
          data: [
            { id: 'story-1', status: 'done' },
            { id: 'story-2', status: 'in_progress' }
          ]
        });

        const result = await tools.close_epic.execute(
          {
            epicId: 'epic-123',
            closedBy: 'john.doe@example.com'
          },
          createContext('close_epic')
        );

        expect(result.success).toBe(false);
        expect(result.error).toBeDefined();
        expect(result.error.message).toContain('Epic cannot be closed with only 50% completion');
      });
    });

    describe('get_epic_stories', () => {
      it('should list stories with filtering', async () => {
        mockDb.get.mockResolvedValueOnce({
          success: true,
          data: {
            id: 'epic-123',
            title: 'Test Epic',
            status: 'active'
          }
        });

        mockDb.query.mockResolvedValueOnce({
          success: true,
          data: [
            { 
              id: 'story-1',
              title: 'Story 1',
              status: 'done',
              story_points: 5,
              assignee: 'dev1@example.com',
              acceptance_criteria: '[]',
              tags: '[]',
              created_at: Math.floor(Date.now() / 1000),
              updated_at: Math.floor(Date.now() / 1000),
              sprint_id: null
            },
            { 
              id: 'story-2',
              title: 'Story 2',
              status: 'done',
              story_points: 3,
              assignee: 'dev2@example.com',
              acceptance_criteria: '[]',
              tags: '[]',
              created_at: Math.floor(Date.now() / 1000),
              updated_at: Math.floor(Date.now() / 1000),
              sprint_id: null
            }
          ]
        });

        const result = await tools.get_epic_stories.execute(
          {
            epicId: 'epic-123',
            status: 'done'
          },
          createContext('get_epic_stories')
        );

        expect(result.success).toBe(true);
        expect(result.data).toMatchObject({
          epicId: 'epic-123',
          epicTitle: 'Test Epic',
          stories: expect.any(Array),
          summary: expect.objectContaining({
            total: 2,
            byStatus: expect.any(Object)
          })
        });
      });
    });

    describe('validate_epic_requirements', () => {
      it('should validate epic completeness', async () => {
        mockDb.get.mockResolvedValueOnce({
          success: true,
          data: {
            id: 'epic-123',
            title: 'Epic to Validate',
            description: 'Test epic',
            success_criteria: JSON.stringify(['Criterion 1', 'Criterion 2'])
          }
        });

        mockDb.query.mockResolvedValueOnce({
          success: true,
          data: [
            { id: 'story-1', acceptance_criteria: JSON.stringify(['AC1', 'AC2']) },
            { id: 'story-2', acceptance_criteria: JSON.stringify(['AC3']) }
          ]
        });

        const result = await tools.validate_epic_requirements.execute(
          { epicId: 'epic-123' },
          createContext('validate_epic_requirements')
        );

        expect(result.success).toBe(true);
        expect(result.data).toMatchObject({
          epicId: 'epic-123',
          epicTitle: 'Epic to Validate',
          overallValid: false, // Not 80% complete
          validationResults: expect.any(Array),
          summary: expect.objectContaining({
            totalChecks: expect.any(Number),
            passed: expect.any(Number),
            failed: expect.any(Number)
          })
        });
      });

      it('should identify validation issues', async () => {
        mockDb.get.mockResolvedValueOnce({
          success: true,
          data: {
            id: 'epic-123',
            title: 'Incomplete Epic',
            description: null,
            success_criteria: '[]'
          }
        });

        mockDb.query.mockResolvedValueOnce({
          success: true,
          data: []
        });

        const result = await tools.validate_epic_requirements.execute(
          { epicId: 'epic-123' },
          createContext('validate_epic_requirements')
        );

        expect(result.success).toBe(true);
        expect(result.data.overallValid).toBe(false);
        
        const failedChecks = result.data.validationResults.filter(r => !r.passed);
        const failedMessages = failedChecks.map(r => r.message);
        
        expect(failedMessages).toContain('Epic description is missing or empty');
        expect(failedMessages).toContain('No success criteria defined');
        expect(failedMessages).toContain('Epic has no linked stories');
      });
    });

    describe('generate_epic_report', () => {
      it('should generate comprehensive epic report', async () => {
        mockDb.get.mockResolvedValueOnce({
          success: true,
          data: {
            id: 'epic-123',
            title: 'Reporting Epic',
            description: 'Epic for reporting',
            status: 'active',
            created_at: new Date('2025-01-01').getTime() / 1000
          }
        });

        mockDb.query
          .mockResolvedValueOnce({ // Stories
            success: true,
            data: [
              { status: 'done', story_points: 5 },
              { status: 'in_progress', story_points: 3 }
            ]
          })
          .mockResolvedValueOnce({ // Timeline data
            success: true,
            data: [
              { sprint_name: 'Sprint 1', story_count: 2, total_points: 8 }
            ]
          });

        const result = await tools.generate_epic_report.execute(
          { epicId: 'epic-123' },
          createContext('generate_epic_report')
        );

        expect(result.success).toBe(true);
        expect(result.data).toMatchObject({
          epicId: 'epic-123',
          epicTitle: 'Reporting Epic',
          epicStatus: 'active',
          metrics: expect.objectContaining({
            stories: expect.objectContaining({
              total: 2,
              completed: 1,
              completionRate: 50
            }),
            storyPoints: expect.objectContaining({
              total: 8,
              completed: 5,
              completionRate: 63
            })
          }),
          timeline: expect.any(Array),
          summary: expect.objectContaining({
            readyForClosure: false,
            keyMetrics: expect.any(Object)
          })
        });
      });
    });
  });

  describe('Advanced Reporting Tools', () => {
    describe('generate_sprint_report', () => {
      it('should generate comprehensive sprint analysis', async () => {
        const sprintId = 'sprint-123';
        
        // Mock sprint data
        mockDb.get.mockResolvedValueOnce({
          success: true,
          data: {
            id: sprintId,
            name: 'Sprint 10',
            goal: 'Complete authentication features',
            status: 'active',
            story_points_planned: 21,
            story_points_completed: 13,
            start_date: new Date('2025-01-01').getTime() / 1000,
            end_date: new Date('2025-01-14').getTime() / 1000
          }
        });

        // Mock stories
        mockDb.query
          .mockResolvedValueOnce({
            success: true,
            data: [
              { status: 'done', story_points: 5, assignee: 'dev1' },
              { status: 'done', story_points: 8, assignee: 'dev2' },
              { status: 'in_progress', story_points: 3, assignee: 'dev1' },
              { status: 'todo', story_points: 5, assignee: null }
            ]
          })
          .mockResolvedValueOnce({ // Blockers
            success: true,
            data: [
              { story_id: 'story-3', reason: 'Waiting for API specs' }
            ]
          });

        const result = await tools.generate_sprint_report.execute(
          { sprintId },
          createContext('generate_sprint_report')
        );

        expect(result.success).toBe(true);
        expect(result.data).toMatchObject({
          sprintId,
          sprintName: 'Sprint 10',
          sprintGoal: 'Complete authentication features',
          storyMetrics: expect.objectContaining({
            total: 4,
            completed: 2,
            completionRate: 50
          }),
          storyPointMetrics: expect.objectContaining({
            total: 21,
            completed: 13,
            completionRate: 62
          }),
          velocityMetrics: expect.objectContaining({
            planned: 21,
            actual: 13,
            percentage: 62
          }),
          blockerAnalysis: expect.objectContaining({
            total: 1,
            details: expect.arrayContaining([
              expect.objectContaining({
                reason: 'Waiting for API specs'
              })
            ])
          })
        });
      });
    });

    describe('get_team_velocity_trend', () => {
      it('should analyze velocity trends over multiple sprints', async () => {
        mockDb.query.mockResolvedValueOnce({
          success: true,
          data: [
            { 
              id: 'sprint-1',
              name: 'Sprint 1',
              status: 'completed',
              duration: 14,
              start_date: new Date('2025-01-01').getTime() / 1000,
              end_date: new Date('2025-01-14').getTime() / 1000,
              total_stories: 5,
              total_points: 25,
              completed_points: 21
            },
            { 
              id: 'sprint-2',
              name: 'Sprint 2',
              status: 'completed',
              duration: 14,
              start_date: new Date('2025-01-15').getTime() / 1000,
              end_date: new Date('2025-01-28').getTime() / 1000,
              total_stories: 4,
              total_points: 20,
              completed_points: 18
            },
            { 
              id: 'sprint-3',
              name: 'Sprint 3',
              status: 'completed',
              duration: 14,
              start_date: new Date('2025-01-29').getTime() / 1000,
              end_date: new Date('2025-02-11').getTime() / 1000,
              total_stories: 6,
              total_points: 30,
              completed_points: 24
            }
          ]
        });

        const result = await tools.get_team_velocity_trend.execute(
          { numberOfSprints: 3 },
          createContext('get_team_velocity_trend')
        );

        expect(result.success).toBe(true);
        expect(result.data).toMatchObject({
          velocityTrend: expect.arrayContaining([
            expect.objectContaining({
              sprintId: 'sprint-1',
              completedPoints: 21
            })
          ]),
          summary: expect.objectContaining({
            averageVelocity: 21,
            trendDirection: expect.any(String),
            consistency: expect.any(Object)
          })
        });
      });
    });

    describe('get_cycle_time_metrics', () => {
      it('should analyze story lifecycle and identify bottlenecks', async () => {
        mockDb.query.mockResolvedValueOnce({
          success: true,
          data: [
            {
              story_id: 'story-1',
              previous_status: 'todo',
              new_status: 'in_progress',
              changed_at: new Date('2025-01-01').getTime() / 1000
            },
            {
              story_id: 'story-1',
              previous_status: 'in_progress',
              new_status: 'review',
              changed_at: new Date('2025-01-03').getTime() / 1000
            },
            {
              story_id: 'story-1',
              previous_status: 'review',
              new_status: 'done',
              changed_at: new Date('2025-01-04').getTime() / 1000
            }
          ]
        });

        const result = await tools.get_cycle_time_metrics.execute(
          { dateRange: { start: '2024-12-01', end: '2025-01-04' } },
          createContext('get_cycle_time_metrics')
        );

        expect(result.success).toBe(true);
        expect(result.data).toMatchObject({
          stories: expect.any(Array),
          summary: expect.objectContaining({
            totalStories: expect.any(Number),
            completedStories: expect.any(Number),
            averageCycleTimeDays: expect.any(Number),
            bottlenecks: expect.any(Object)
          })
        });
      });
    });

    describe('generate_retrospective_template', () => {
      it('should generate retrospective template with sprint data', async () => {
        const sprintId = 'sprint-123';
        
        mockDb.get.mockResolvedValueOnce({
          success: true,
          data: {
            id: sprintId,
            name: 'Sprint 10',
            goal: 'Complete user features',
            story_points_planned: 21,
            story_points_completed: 18
          }
        });

        mockDb.query
          .mockResolvedValueOnce({ // Completed stories
            success: true,
            data: [
              { title: 'User login', story_points: 5, status: 'done' },
              { title: 'User profile', story_points: 8, status: 'done' }
            ]
          })
          .mockResolvedValueOnce({ // Blockers
            success: true,
            data: [
              { story_title: 'Password reset', reason: 'API dependencies' }
            ]
          });

        const result = await tools.generate_retrospective_template.execute(
          { 
            sprintId,
            templateType: 'starfish'
          },
          createContext('generate_retrospective_template')
        );

        expect(result.success).toBe(true);
        expect(result.data).toMatchObject({
          sprintId,
          sprintName: 'Sprint 10',
          templateType: 'starfish',
          template: expect.objectContaining({
            title: expect.any(String),
            sections: expect.any(Array)
          }),
          metrics: expect.objectContaining({
            totalStories: 2,
            completedStories: 2,
            totalStoryPoints: 13,
            completedStoryPoints: 13
          })
        });
      });
    });

    describe('generate_epic_burndown', () => {
      it('should track epic progress over time', async () => {
        mockDb.get.mockResolvedValueOnce({
          success: true,
          data: {
            id: 'epic-123',
            title: 'Feature Epic',
            created_at: new Date('2025-01-01').getTime() / 1000
          }
        });

        // Mock epic metrics
        mockDb.query.mockResolvedValueOnce({
          success: true,
          data: [
            {
              calculation_date: new Date('2025-01-07').getTime() / 1000,
              stories_total: 4,
              stories_completed: 1,
              story_points_total: 21,
              story_points_completed: 5,
              completion_percentage: 25
            },
            {
              calculation_date: new Date('2025-01-14').getTime() / 1000,
              stories_total: 4,
              stories_completed: 2,
              story_points_total: 21,
              story_points_completed: 13,
              completion_percentage: 50
            }
          ]
        });

        const result = await tools.generate_epic_burndown.execute(
          { epicId: 'epic-123' },
          createContext('generate_epic_burndown')
        );

        expect(result.success).toBe(true);
        expect(result.data).toMatchObject({
          epicId: 'epic-123',
          epicTitle: 'Feature Epic',
          chartData: expect.any(Array),
          summary: expect.any(Object)
        });
      });
    });

    describe('get_cross_sprint_analytics', () => {
      it('should analyze patterns across multiple sprints', async () => {
        mockDb.query
          .mockResolvedValueOnce({ // Sprint data
            success: true,
            data: [
              {
                id: 'sprint-1',
                name: 'Sprint 1',
                story_points_planned: 21,
                story_points_completed: 18,
                stories_total: 5,
                stories_completed: 4
              },
              {
                id: 'sprint-2',
                name: 'Sprint 2',
                story_points_planned: 24,
                story_points_completed: 24,
                stories_total: 6,
                stories_completed: 6
              }
            ]
          })
          .mockResolvedValueOnce({ // Story type distribution
            success: true,
            data: [
              { tag: 'feature', count: 8, total_points: 32 },
              { tag: 'bug', count: 3, total_points: 10 }
            ]
          });

        const result = await tools.get_cross_sprint_analytics.execute(
          { numberOfSprints: 2 },
          createContext('get_cross_sprint_analytics')
        );

        expect(result.success).toBe(true);
        expect(result.data).toMatchObject({
          analysisRange: 2,
          sprints: expect.any(Array),
          analytics: expect.objectContaining({
            sprintCount: 2,
            velocityTrend: expect.any(Object),
            storyTrend: expect.any(Object),
            predictability: expect.any(Object)
          })
        });
      });
    });

    describe('get_dependency_report', () => {
      it('should analyze cross-story dependencies', async () => {
        mockDb.query.mockResolvedValueOnce({ // Blocked stories
          success: true,
          data: [
            {
              blocker_id: 'blocker-1',
              blocker_type: 'dependency',
              reason: 'Waiting for API endpoints',
              blocker_status: 'active',
              resolution: null,
              resolved_by: null,
              resolved_at: null,
              blocked_story_id: 'story-1',
              blocked_story_title: 'API Integration',
              blocked_story_status: 'blocked',
              blocker_story_id: 'story-2',
              blocker_story_title: 'API Development',
              blocker_story_status: 'in_progress'
            },
            {
              blocker_id: 'blocker-2',
              blocker_type: 'external',
              reason: 'Waiting for design approval',
              blocker_status: 'active',
              resolution: null,
              resolved_by: null,
              resolved_at: null,
              blocked_story_id: 'story-3',
              blocked_story_title: 'UI Updates',
              blocked_story_status: 'blocked',
              blocker_story_id: null,
              blocker_story_title: null,
              blocker_story_status: null
            }
          ]
        });

        const result = await tools.get_dependency_report.execute(
          {},
          createContext('get_dependency_report')
        );

        expect(result.success).toBe(true);
        expect(result.data).toMatchObject({
          dependencies: expect.arrayContaining([
            expect.objectContaining({
              type: 'dependency',
              reason: 'Waiting for API endpoints',
              blockedStory: expect.objectContaining({
                title: 'API Integration'
              })
            })
          ]),
          analysis: expect.objectContaining({
            total: 2,
            active: 2,
            byType: expect.any(Object),
            criticalPath: expect.any(Array)
          })
        });
      });
    });
  });

  describe('Error Handling', () => {
    it('should handle database errors gracefully', async () => {
      // Mock get to throw an error
      mockDb.get.mockImplementation(() => {
        throw new Error('Database connection failed');
      });

      const result = await tools.get_epic_progress.execute(
        { epicId: 'epic-123' },
        createContext('get_epic_progress')
      );

      expect(result.success).toBe(false);
      expect(result.error.message).toContain('Failed to get epic progress');
    });

    it('should validate required parameters', async () => {
      const result = await tools.get_epic_progress.execute(
        {}, // Missing epicId
        createContext('get_epic_progress')
      );

      expect(result.success).toBe(false);
      expect(result.error.message).toBeDefined();
      expect(result.error.category).toBe('validation');
    });
  });
});