import { describe, it, expect, beforeEach, vi } from 'vitest';
import { randomUUID } from 'crypto';
import { setupAgileManagement } from '../index';
import type { Server } from '@modelcontextprotocol/sdk/types.js';
import type { RequestContext } from '../../../core/types';

describe('Workflow Validation', () => {
  let mockServer: Server;
  let mockContext: RequestContext;
  let tools: any;
  let mockDb: any;

  beforeEach(async () => {
    // Mock server
    mockServer = {
      setRequestHandler: vi.fn()
    } as any;

    // Mock database
    mockDb = {
      run: vi.fn(),
      get: vi.fn(),
      query: vi.fn()
    };

    // Mock context
    mockContext = {
      projectId: 'test-project',
      userId: 'test-user',
      db: mockDb
    } as any;

    // Setup tools
    const setup = await setupAgileManagement(mockServer);
    tools = setup.tools.reduce((acc, tool) => {
      acc[tool.name] = tool;
      return acc;
    }, {} as any);
  });

  describe('Todo to In-Progress Workflow', () => {
    it('should allow transition from todo to in_progress', async () => {
      // Mock story in todo status
      mockDb.get.mockResolvedValue({
        success: true,
        data: { id: 'story-1', title: 'Test Story', status: 'todo' }
      });

      mockDb.run.mockResolvedValue({
        success: true,
        data: { changes: 1 }
      });

      const result = await tools.update_story_status.execute({
        storyId: 'story-1',
        status: 'in_progress',
        notes: 'Starting development'
      }, mockContext);

      expect(result.success).toBe(true);
      expect(result.data.oldStatus).toBe('todo');
      expect(result.data.newStatus).toBe('in_progress');
      expect(result.data.workflowValidationBypassed).toBe(false);
    });


    it('should block transition from review to in_progress', async () => {
      // Mock story in review status
      mockDb.get.mockResolvedValue({
        success: true,
        data: { id: 'story-1', title: 'Test Story', status: 'review' }
      });

      const result = await tools.update_story_status.execute({
        storyId: 'story-1',
        status: 'in_progress',
        notes: 'Going back to development'
      }, mockContext);

      expect(result.success).toBe(false);
      expect(result.error.code).toBe('WORKFLOW_VALIDATION_ERROR');
      expect(result.error.message).toContain('Cannot transition from "review" to "in_progress"');
      expect(result.error.details.suggestedAction).toContain('Stories must be in "todo" status');
    });

    it('should allow transition from done to in_progress with workflow validation bypass', async () => {
      // Mock story in done status
      mockDb.get.mockResolvedValue({
        success: true,
        data: { id: 'story-1', title: 'Test Story', status: 'done' }
      });

      mockDb.run.mockResolvedValue({
        success: true,
        data: { changes: 1 }
      });

      const result = await tools.update_story_status.execute({
        storyId: 'story-1',
        status: 'in_progress',
        notes: 'Reopening for more work',
        skipWorkflowValidation: true
      }, mockContext);

      expect(result.success).toBe(true);
      expect(result.data.workflowValidationBypassed).toBe(true);
      
      // Should log bypass in history
      expect(mockDb.run).toHaveBeenCalledWith(
        expect.stringContaining('INSERT INTO story_history'),
        expect.arrayContaining([
          expect.any(String), // id
          'story-1',
          'status_change',
          'done',
          'in_progress',
          'Reopening for more work [WORKFLOW VALIDATION BYPASSED]',
          'test-user',
          expect.any(Number)
        ])
      );
    });
  });


  describe('Other Valid Transitions', () => {
    it('should allow standard workflow transitions', async () => {
      const validTransitions = [
        { from: 'todo', to: 'in_progress' },
        { from: 'in_progress', to: 'review' },
        { from: 'review', to: 'done' },
        { from: 'done', to: 'review' }, // Can reopen
        { from: 'blocked', to: 'todo' },
        { from: 'blocked', to: 'in_progress' }
      ];

      for (const transition of validTransitions) {
        mockDb.get.mockResolvedValue({
          success: true,
          data: { id: 'story-1', title: 'Test Story', status: transition.from }
        });

        mockDb.run.mockResolvedValue({
          success: true,
          data: { changes: 1 }
        });

        const result = await tools.update_story_status.execute({
          storyId: 'story-1',
          status: transition.to,
          notes: `Moving from ${transition.from} to ${transition.to}`
        }, mockContext);

        if (!result.success) {
          console.error(`Failed transition: ${transition.from} -> ${transition.to}`, result.error);
        }
        expect(result.success).toBe(true, 
          `Transition from ${transition.from} to ${transition.to} should be valid`);
      }
    });
  });

  describe('Invalid Transitions', () => {
    it('should block invalid transitions', async () => {
      const invalidTransitions = [
        { from: 'todo', to: 'review' },
        { from: 'todo', to: 'done' },
        { from: 'in_progress', to: 'todo' },
        { from: 'review', to: 'todo' }
      ];

      for (const transition of invalidTransitions) {
        mockDb.get.mockResolvedValue({
          success: true,
          data: { id: 'story-1', title: 'Test Story', status: transition.from }
        });

        const result = await tools.update_story_status.execute({
          storyId: 'story-1',
          status: transition.to,
          notes: `Trying invalid transition`
        }, mockContext);

        expect(result.success).toBe(false, 
          `Transition from ${transition.from} to ${transition.to} should be blocked`);
        expect(result.error.code).toBe('WORKFLOW_VALIDATION_ERROR');
      }
    });
  });

  describe('Admin Override', () => {
    it('should allow any transition with skipWorkflowValidation', async () => {
      // Test the most restricted case: todo -> in_progress
      mockDb.get.mockResolvedValue({
        success: true,
        data: { id: 'story-1', title: 'Test Story', status: 'todo' }
      });

      mockDb.run.mockResolvedValue({
        success: true,
        data: { changes: 1 }
      });

      const result = await tools.update_story_status.execute({
        storyId: 'story-1',
        status: 'in_progress',
        notes: 'Emergency fix, bypassing workflow',
        skipWorkflowValidation: true
      }, mockContext);

      expect(result.success).toBe(true);
      expect(result.data.workflowValidationBypassed).toBe(true);
      
      // Should log the bypass
      expect(mockDb.run).toHaveBeenCalledWith(
        expect.stringContaining('INSERT INTO story_history'),
        expect.arrayContaining([
          expect.any(String),
          'story-1',
          'status_change',
          'todo',
          'in_progress',
          'Emergency fix, bypassing workflow [WORKFLOW VALIDATION BYPASSED]',
          'test-user',
          expect.any(Number)
        ])
      );
    });
  });

  describe('Same Status Transitions', () => {
    it('should allow same status transitions', async () => {
      mockDb.get.mockResolvedValue({
        success: true,
        data: { id: 'story-1', title: 'Test Story', status: 'in_progress' }
      });

      mockDb.run.mockResolvedValue({
        success: true,
        data: { changes: 1 }
      });

      const result = await tools.update_story_status.execute({
        storyId: 'story-1',
        status: 'in_progress',
        notes: 'Updating with progress notes'
      }, mockContext);

      expect(result.success).toBe(true);
      expect(result.data.oldStatus).toBe('in_progress');
      expect(result.data.newStatus).toBe('in_progress');
    });
  });

  describe('Error Cases', () => {
    it('should handle story not found', async () => {
      mockDb.get.mockResolvedValue({
        success: false,
        data: null
      });

      const result = await tools.update_story_status.execute({
        storyId: 'nonexistent-story',
        status: 'in_progress'
      }, mockContext);

      expect(result.success).toBe(false);
      expect(result.error.code).toBe('RESOURCE_NOT_FOUND');
    });

    it('should handle database errors', async () => {
      mockDb.get.mockResolvedValue({
        success: true,
        data: { id: 'story-1', title: 'Test Story', status: 'todo' }
      });

      mockDb.run.mockResolvedValue({
        success: false,
        error: 'Database connection failed'
      });

      const result = await tools.update_story_status.execute({
        storyId: 'story-1',
        status: 'in_progress'
      }, mockContext);

      expect(result.success).toBe(false);
      expect(result.error.code).toBe('DATABASE_ERROR');
    });
  });
});