import { describe, it, expect, beforeEach, vi } from 'vitest';
import { ADRWizard } from '../adr-wizard.js';
import { ADRManager } from '../adr-manager.js';
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { CallToolRequest } from '@modelcontextprotocol/sdk/types.js';
import type { ADR, CreateADROptions } from '../types.js';

// Mock the ADRManager
vi.mock('../adr-manager.js', () => ({
  ADRManager: vi.fn().mockImplementation(() => ({
    initialize: vi.fn().mockResolvedValue(undefined),
    createADR: vi.fn(),
    listADRs: vi.fn().mockResolvedValue([]),
    getADR: vi.fn()
  }))
}));

describe('ADRWizard', () => {
  let wizard: ADRWizard;
  let mockManager: any;
  let mockServer: Server;
  let sendProgressCallback: any;

  const testADR: ADR = {
    id: 'ADR-0001',
    title: 'Use TypeScript',
    status: 'proposed',
    date: new Date('2024-01-15'),
    deciders: ['John Doe'],
    template: 'nygard',
    context: 'Need type safety',
    decision: 'Use TypeScript',
    consequences: 'Learning curve',
    createdAt: new Date('2024-01-15'),
    updatedAt: new Date('2024-01-15'),
    statusHistory: []
  };

  beforeEach(() => {
    mockServer = {} as Server;
    wizard = new ADRWizard({} as any);
    mockManager = (wizard as any).manager;
    sendProgressCallback = vi.fn();
  });

  describe('startInteractiveADR', () => {
    it('should start interactive ADR creation', async () => {
      const result = await wizard.startInteractiveADR(
        mockServer,
        { sessionId: 'test-session' } as CallToolRequest,
        sendProgressCallback
      );

      expect(result).toMatchObject({
        content: expect.arrayContaining([{
          type: 'text',
          text: expect.stringContaining('ADR Creation Wizard')
        }])
      });
      expect((wizard as any).sessions.has('test-session')).toBe(true);
    });

    it('should ask for ADR title in step 1', async () => {
      await wizard.startInteractiveADR(
        mockServer,
        { sessionId: 'test-session' } as CallToolRequest,
        sendProgressCallback
      );

      const session = (wizard as any).sessions.get('test-session');
      expect(session.currentStep).toBe(1);
      expect(session.data).toEqual({});
    });
  });

  describe('continueInteractiveADR', () => {
    beforeEach(async () => {
      await wizard.startInteractiveADR(
        mockServer,
        { sessionId: 'test-session' } as CallToolRequest,
        sendProgressCallback
      );
    });

    it('should collect title and move to template selection', async () => {
      const result = await wizard.continueInteractiveADR(
        mockServer,
        { sessionId: 'test-session' } as CallToolRequest,
        sendProgressCallback,
        { userInput: 'Use React for Frontend' }
      );

      const session = (wizard as any).sessions.get('test-session');
      expect(session.data.title).toBe('Use React for Frontend');
      expect(session.currentStep).toBe(2);
      expect(result.content[0].text).toContain('template');
    });

    it('should collect template and move to context', async () => {
      // Set title first
      await wizard.continueInteractiveADR(
        mockServer,
        { sessionId: 'test-session' } as CallToolRequest,
        sendProgressCallback,
        { userInput: 'Use React' }
      );

      const result = await wizard.continueInteractiveADR(
        mockServer,
        { sessionId: 'test-session' } as CallToolRequest,
        sendProgressCallback,
        { userInput: '1' } // Choose Nygard template
      );

      const session = (wizard as any).sessions.get('test-session');
      expect(session.data.template).toBe('nygard');
      expect(session.currentStep).toBe(3);
      expect(result.content[0].text).toContain('context');
    });

    it('should handle MADR template with additional fields', async () => {
      // Set title
      await wizard.continueInteractiveADR(
        mockServer,
        { sessionId: 'test-session' } as CallToolRequest,
        sendProgressCallback,
        { userInput: 'Choose Database' }
      );

      // Choose MADR template
      await wizard.continueInteractiveADR(
        mockServer,
        { sessionId: 'test-session' } as CallToolRequest,
        sendProgressCallback,
        { userInput: '2' } // MADR
      );

      const session = (wizard as any).sessions.get('test-session');
      expect(session.data.template).toBe('madr');
      expect(session.totalSteps).toBeGreaterThan(8); // MADR has more steps
    });

    it('should complete ADR creation with all data', async () => {
      mockManager.createADR.mockResolvedValueOnce(testADR);

      // Go through all steps
      await wizard.continueInteractiveADR(
        mockServer,
        { sessionId: 'test-session' } as CallToolRequest,
        sendProgressCallback,
        { userInput: 'Use React for Frontend' }
      );

      await wizard.continueInteractiveADR(
        mockServer,
        { sessionId: 'test-session' } as CallToolRequest,
        sendProgressCallback,
        { userInput: '1' } // Nygard
      );

      await wizard.continueInteractiveADR(
        mockServer,
        { sessionId: 'test-session' } as CallToolRequest,
        sendProgressCallback,
        { userInput: 'We need a modern frontend framework' }
      );

      await wizard.continueInteractiveADR(
        mockServer,
        { sessionId: 'test-session' } as CallToolRequest,
        sendProgressCallback,
        { userInput: 'We will use React for the frontend' }
      );

      await wizard.continueInteractiveADR(
        mockServer,
        { sessionId: 'test-session' } as CallToolRequest,
        sendProgressCallback,
        { userInput: 'Team needs React training, JSX learning curve' }
      );

      await wizard.continueInteractiveADR(
        mockServer,
        { sessionId: 'test-session' } as CallToolRequest,
        sendProgressCallback,
        { userInput: 'John Doe, Jane Smith' }
      );

      await wizard.continueInteractiveADR(
        mockServer,
        { sessionId: 'test-session' } as CallToolRequest,
        sendProgressCallback,
        { userInput: 'frontend, react, ui' }
      );

      const result = await wizard.continueInteractiveADR(
        mockServer,
        { sessionId: 'test-session' } as CallToolRequest,
        sendProgressCallback,
        { userInput: 'yes' } // Confirm
      );

      expect(mockManager.createADR).toHaveBeenCalledWith({
        title: 'Use React for Frontend',
        template: 'nygard',
        context: 'We need a modern frontend framework',
        decision: 'We will use React for the frontend',
        consequences: 'Team needs React training, JSX learning curve',
        deciders: ['John Doe', 'Jane Smith'],
        tags: ['frontend', 'react', 'ui']
      });

      expect(result.structured.adr).toEqual(testADR);
      expect((wizard as any).sessions.has('test-session')).toBe(false);
    });

    it('should handle cancellation', async () => {
      await wizard.continueInteractiveADR(
        mockServer,
        { sessionId: 'test-session' } as CallToolRequest,
        sendProgressCallback,
        { userInput: 'Use React' }
      );

      const result = await wizard.continueInteractiveADR(
        mockServer,
        { sessionId: 'test-session' } as CallToolRequest,
        sendProgressCallback,
        { userInput: 'cancel' }
      );

      expect(result.content[0].text).toContain('cancelled');
      expect((wizard as any).sessions.has('test-session')).toBe(false);
    });

    it('should handle editing summary', async () => {
      // Complete all steps
      const steps = [
        'Use React for Frontend',
        '1', // Nygard
        'Need modern UI',
        'Use React',
        'Training needed',
        'John Doe',
        'frontend'
      ];

      for (const input of steps) {
        await wizard.continueInteractiveADR(
          mockServer,
          { sessionId: 'test-session' } as CallToolRequest,
          sendProgressCallback,
          { userInput: input }
        );
      }

      // Choose to edit
      const result = await wizard.continueInteractiveADR(
        mockServer,
        { sessionId: 'test-session' } as CallToolRequest,
        sendProgressCallback,
        { userInput: 'edit' }
      );

      expect(result.content[0].text).toContain('Which field');
    });

    it('should collect decision drivers for MADR template', async () => {
      // Set up MADR template
      await wizard.continueInteractiveADR(
        mockServer,
        { sessionId: 'test-session' } as CallToolRequest,
        sendProgressCallback,
        { userInput: 'Choose Database' }
      );

      await wizard.continueInteractiveADR(
        mockServer,
        { sessionId: 'test-session' } as CallToolRequest,
        sendProgressCallback,
        { userInput: '2' } // MADR
      );

      // Skip to decision drivers step - need to go through context, decision, consequences
      await wizard.continueInteractiveADR(
        mockServer,
        { sessionId: 'test-session' } as CallToolRequest,
        sendProgressCallback,
        { userInput: 'Test context' }
      );

      await wizard.continueInteractiveADR(
        mockServer,
        { sessionId: 'test-session' } as CallToolRequest,
        sendProgressCallback,
        { userInput: 'Test decision' }
      );

      await wizard.continueInteractiveADR(
        mockServer,
        { sessionId: 'test-session' } as CallToolRequest,
        sendProgressCallback,
        { userInput: 'Test consequences' }
      );

      // Now we should be at decision drivers step
      const result = await wizard.continueInteractiveADR(
        mockServer,
        { sessionId: 'test-session' } as CallToolRequest,
        sendProgressCallback,
        { userInput: 'Performance, Scalability, Cost' }
      );

      const session = (wizard as any).sessions.get('test-session');
      expect(session.data.decisionDrivers).toEqual(['Performance', 'Scalability', 'Cost']);
    });

    it('should collect considered options for MADR', async () => {
      // Set up MADR template
      await wizard.continueInteractiveADR(
        mockServer,
        { sessionId: 'test-session' } as CallToolRequest,
        sendProgressCallback,
        { userInput: 'Choose Database' }
      );

      await wizard.continueInteractiveADR(
        mockServer,
        { sessionId: 'test-session' } as CallToolRequest,
        sendProgressCallback,
        { userInput: '2' } // MADR
      );

      // Go through the steps to get to options
      await wizard.continueInteractiveADR(
        mockServer,
        { sessionId: 'test-session' } as CallToolRequest,
        sendProgressCallback,
        { userInput: 'Test context' }
      );

      await wizard.continueInteractiveADR(
        mockServer,
        { sessionId: 'test-session' } as CallToolRequest,
        sendProgressCallback,
        { userInput: 'Test decision' }
      );

      await wizard.continueInteractiveADR(
        mockServer,
        { sessionId: 'test-session' } as CallToolRequest,
        sendProgressCallback,
        { userInput: 'Test consequences' }
      );

      await wizard.continueInteractiveADR(
        mockServer,
        { sessionId: 'test-session' } as CallToolRequest,
        sendProgressCallback,
        { userInput: 'Performance, Cost' } // Decision drivers
      );

      await wizard.continueInteractiveADR(
        mockServer,
        { sessionId: 'test-session' } as CallToolRequest,
        sendProgressCallback,
        { userInput: 'yes' } // Yes to considered options
      );

      // Add first option
      await wizard.continueInteractiveADR(
        mockServer,
        { sessionId: 'test-session' } as CallToolRequest,
        sendProgressCallback,
        { userInput: 'PostgreSQL' }
      );

      await wizard.continueInteractiveADR(
        mockServer,
        { sessionId: 'test-session' } as CallToolRequest,
        sendProgressCallback,
        { userInput: 'Relational database with ACID compliance' }
      );

      await wizard.continueInteractiveADR(
        mockServer,
        { sessionId: 'test-session' } as CallToolRequest,
        sendProgressCallback,
        { userInput: 'ACID compliant, Mature, Great performance' }
      );

      await wizard.continueInteractiveADR(
        mockServer,
        { sessionId: 'test-session' } as CallToolRequest,
        sendProgressCallback,
        { userInput: 'Complex setup, Requires SQL knowledge' }
      );

      const result = await wizard.continueInteractiveADR(
        mockServer,
        { sessionId: 'test-session' } as CallToolRequest,
        sendProgressCallback,
        { userInput: 'done' }
      );

      const session = (wizard as any).sessions.get('test-session');
      expect(session.data.consideredOptions).toHaveLength(1);
      expect(session.data.consideredOptions[0]).toMatchObject({
        title: 'PostgreSQL',
        description: 'Relational database with ACID compliance',
        pros: ['ACID compliant', 'Mature', 'Great performance'],
        cons: ['Complex setup', 'Requires SQL knowledge']
      });
    });

    it('should handle Y-statement template', async () => {
      await wizard.continueInteractiveADR(
        mockServer,
        { sessionId: 'test-session' } as CallToolRequest,
        sendProgressCallback,
        { userInput: 'Improve Performance' }
      );

      await wizard.continueInteractiveADR(
        mockServer,
        { sessionId: 'test-session' } as CallToolRequest,
        sendProgressCallback,
        { userInput: '3' } // Y-statement
      );

      const session = (wizard as any).sessions.get('test-session');
      expect(session.data.template).toBe('y-statement');
      // Y-statement should have fewer steps
      expect(session.totalSteps).toBeLessThan(10);
    });

    it('should validate required fields', async () => {
      const result = await wizard.continueInteractiveADR(
        mockServer,
        { sessionId: 'test-session' } as CallToolRequest,
        sendProgressCallback,
        { userInput: '' } // Empty title
      );

      expect(result.content[0].text).toContain('cannot be empty');
      const session = (wizard as any).sessions.get('test-session');
      expect(session.currentStep).toBe(1); // Should stay on same step
    });

    it('should handle related ADRs', async () => {
      mockManager.listADRs.mockResolvedValueOnce([
        { id: 'ADR-0001', title: 'Use React', status: 'accepted' },
        { id: 'ADR-0002', title: 'Use TypeScript', status: 'accepted' }
      ]);

      // Complete basic steps
      const steps = ['New ADR', '1', 'Context', 'Decision', 'Consequences', 'John'];
      for (const input of steps) {
        await wizard.continueInteractiveADR(
          mockServer,
          { sessionId: 'test-session' } as CallToolRequest,
          sendProgressCallback,
          { userInput: input }
        );
      }

      // Add tags step
      await wizard.continueInteractiveADR(
        mockServer,
        { sessionId: 'test-session' } as CallToolRequest,
        sendProgressCallback,
        { userInput: 'test' } // tags
      );

      // Now should be at related ADRs step
      const result = await wizard.continueInteractiveADR(
        mockServer,
        { sessionId: 'test-session' } as CallToolRequest,
        sendProgressCallback,
        { userInput: 'ADR-0001' }
      );

      const session = (wizard as any).sessions.get('test-session');
      expect(session.data.relatedTo).toEqual(['ADR-0001']);
    });

    it('should handle superseding ADRs', async () => {
      mockManager.listADRs.mockResolvedValueOnce([
        { id: 'ADR-0001', title: 'Use jQuery', status: 'accepted' }
      ]);

      // Complete basic steps
      const steps = ['Replace jQuery', '1', 'Context', 'Decision', 'Consequences', 'John'];
      for (const input of steps) {
        await wizard.continueInteractiveADR(
          mockServer,
          { sessionId: 'test-session' } as CallToolRequest,
          sendProgressCallback,
          { userInput: input }
        );
      }

      // Add tags
      await wizard.continueInteractiveADR(
        mockServer,
        { sessionId: 'test-session' } as CallToolRequest,
        sendProgressCallback,
        { userInput: '' } // no tags
      );

      // Related ADRs
      await wizard.continueInteractiveADR(
        mockServer,
        { sessionId: 'test-session' } as CallToolRequest,
        sendProgressCallback,
        { userInput: '' } // no related
      );

      // Now at supersedes step
      const result = await wizard.continueInteractiveADR(
        mockServer,
        { sessionId: 'test-session' } as CallToolRequest,
        sendProgressCallback,
        { userInput: 'ADR-0001' }
      );

      const session = (wizard as any).sessions.get('test-session');
      expect(session.data.supersedes).toEqual(['ADR-0001']);
    });

    it('should handle invalid session ID', async () => {
      await expect(
        wizard.continueInteractiveADR(
          mockServer,
          { sessionId: 'invalid-session' } as CallToolRequest,
          sendProgressCallback,
          { userInput: 'test' }
        )
      ).rejects.toThrow('No active ADR creation session');
    });

    it('should timeout inactive sessions', async () => {
      vi.useFakeTimers();

      await wizard.startInteractiveADR(
        mockServer,
        { sessionId: 'test-session' } as CallToolRequest,
        sendProgressCallback
      );

      expect((wizard as any).sessions.has('test-session')).toBe(true);

      // Fast forward 31 minutes
      vi.advanceTimersByTime(31 * 60 * 1000);

      // Run cleanup
      (wizard as any).cleanupStaleSessions();

      expect((wizard as any).sessions.has('test-session')).toBe(false);

      vi.useRealTimers();
    });

    it('should provide progress updates', async () => {
      await wizard.continueInteractiveADR(
        mockServer,
        { sessionId: 'test-session' } as CallToolRequest,
        sendProgressCallback,
        { userInput: 'Test ADR' }
      );

      expect(sendProgressCallback).toHaveBeenCalledWith({
        progress: 25,
        total: 100,
        message: expect.any(String)
      });
    });
  });

  describe('cleanupStaleSessions', () => {
    it('should clean up stale sessions', () => {
      vi.useFakeTimers();
      const now = Date.now();

      // Create sessions with different timestamps
      (wizard as any).sessions.set('old-session', {
        lastActivity: now - 35 * 60 * 1000, // 35 minutes ago
        currentStep: 1,
        data: {}
      });

      (wizard as any).sessions.set('new-session', {
        lastActivity: now - 5 * 60 * 1000, // 5 minutes ago
        currentStep: 1,
        data: {}
      });

      (wizard as any).cleanupStaleSessions();

      expect((wizard as any).sessions.has('old-session')).toBe(false);
      expect((wizard as any).sessions.has('new-session')).toBe(true);

      vi.useRealTimers();
    });
  });
});