import { describe, it, expect, beforeEach } from 'vitest';
import { PrioritizationEngine } from '../prioritization-engine.js';
import {
  Feature,
  PrioritizationCriteria,
  PrioritizationResult,
  Initiative,
  ValueMetrics,
  EffortEstimate
} from '../types.js';

describe('PrioritizationEngine', () => {
  let engine: PrioritizationEngine;
  let testFeatures: Feature[];

  beforeEach(() => {
    engine = new PrioritizationEngine();
    
    // Create test features with different characteristics
    testFeatures = [
      {
        id: 'feature-1',
        initiativeId: 'init-1',
        name: 'High Value Low Complexity Feature',
        description: 'A feature with high business value and low complexity',
        userStories: [],
        priority: 0,
        businessValue: {
          score: 90,
          rationale: 'Critical for user retention',
          metrics: ['User retention > 80%', 'Daily active users +50%']
        },
        technicalComplexity: 'low',
        status: 'proposed'
      },
      {
        id: 'feature-2',
        initiativeId: 'init-1',
        name: 'Medium Value Medium Complexity Feature',
        description: 'A balanced feature with medium value and complexity',
        userStories: [],
        priority: 0,
        businessValue: {
          score: 60,
          rationale: 'Nice to have enhancement',
          metrics: ['User satisfaction +10%']
        },
        technicalComplexity: 'medium',
        status: 'proposed'
      },
      {
        id: 'feature-3',
        initiativeId: 'init-1',
        name: 'High Value High Complexity Feature',
        description: 'A valuable but complex feature requiring significant effort',
        userStories: [],
        priority: 0,
        businessValue: {
          score: 85,
          rationale: 'Strategic differentiator',
          metrics: ['Market share +5%', 'Revenue +$1M']
        },
        technicalComplexity: 'very-high',
        status: 'approved'
      },
      {
        id: 'feature-4',
        initiativeId: 'init-1',
        name: 'Low Value Feature',
        description: 'A feature with limited business impact',
        userStories: [],
        priority: 0,
        businessValue: {
          score: 25,
          rationale: 'Minor improvement',
          metrics: ['Support tickets -5%']
        },
        technicalComplexity: 'low',
        status: 'proposed'
      }
    ];
  });

  describe('RICE Scoring', () => {
    it('should prioritize features using RICE method', async () => {
      const criteria: PrioritizationCriteria = {
        method: 'rice'
      };

      const results = await engine.prioritizeFeatures(testFeatures, criteria);

      expect(results).toHaveLength(testFeatures.length);
      expect(results[0].method).toBe('rice');
      
      // Features should be ranked 1 to N
      results.forEach((result, index) => {
        expect(result.rank).toBe(index + 1);
      });

      // High value, low complexity should rank higher
      const highValueLowComplexity = results.find(r => r.featureId === 'feature-1');
      const lowValue = results.find(r => r.featureId === 'feature-4');
      
      expect(highValueLowComplexity!.rank).toBeLessThan(lowValue!.rank);
      expect(highValueLowComplexity!.score).toBeGreaterThan(lowValue!.score);
    });

    it('should include RICE breakdown in results', async () => {
      const criteria: PrioritizationCriteria = {
        method: 'rice'
      };

      const results = await engine.prioritizeFeatures(testFeatures, criteria);
      const result = results[0];

      expect(result.breakdown).toBeDefined();
      expect(result.breakdown.reach).toBeGreaterThan(0);
      expect(result.breakdown.impact).toBeGreaterThan(0);
      expect(result.breakdown.confidence).toBeGreaterThan(0);
      expect(result.breakdown.confidence).toBeLessThanOrEqual(1);
      expect(result.breakdown.effort).toBeGreaterThan(0);
      expect(result.breakdown.score).toBe(
        (result.breakdown.reach * result.breakdown.impact * result.breakdown.confidence) / 
        result.breakdown.effort
      );
    });
  });

  describe('Value-Effort Matrix', () => {
    it('should prioritize features using value-effort matrix', async () => {
      const criteria: PrioritizationCriteria = {
        method: 'value-effort'
      };

      const results = await engine.prioritizeFeatures(testFeatures, criteria);

      expect(results).toHaveLength(testFeatures.length);
      
      // Quick wins (high value, low effort) should rank highest
      const quickWin = results.find(r => r.featureId === 'feature-1');
      expect(quickWin!.rank).toBe(1);
      expect(quickWin!.breakdown.category).toBe('Quick Wins');
    });

    it('should categorize features correctly', async () => {
      const criteria: PrioritizationCriteria = {
        method: 'value-effort'
      };

      const results = await engine.prioritizeFeatures(testFeatures, criteria);

      results.forEach(result => {
        const feature = testFeatures.find(f => f.id === result.featureId);
        expect(result.breakdown.category).toMatch(/Quick Wins|Major Projects|Fill-ins|Time Sinks/);
        
        // Verify categorization logic
        if (feature!.businessValue.score > 60 && 
            ['low', 'medium'].includes(feature!.technicalComplexity)) {
          expect(['Quick Wins', 'Major Projects']).toContain(result.breakdown.category);
        }
      });
    });
  });

  describe('MoSCoW Method', () => {
    it('should prioritize features using MoSCoW method', async () => {
      const criteria: PrioritizationCriteria = {
        method: 'moscow'
      };

      const results = await engine.prioritizeFeatures(testFeatures, criteria);

      expect(results).toHaveLength(testFeatures.length);
      
      results.forEach(result => {
        expect(result.breakdown.category).toMatch(/must-have|should-have|could-have|wont-have/);
        
        // Must-haves should have highest scores
        if (result.breakdown.category === 'must-have') {
          expect(result.score).toBe(100);
        } else if (result.breakdown.category === 'should-have') {
          expect(result.score).toBe(75);
        } else if (result.breakdown.category === 'could-have') {
          expect(result.score).toBe(50);
        } else if (result.breakdown.category === 'wont-have') {
          expect(result.score).toBe(25);
        }
      });
    });
  });

  describe('Kano Model', () => {
    it('should prioritize features using Kano model', async () => {
      // Add features with keywords that trigger Kano categories
      const kanoFeatures: Feature[] = [
        ...testFeatures,
        {
          id: 'feature-security',
          initiativeId: 'init-1',
          name: 'Security Enhancement',
          description: 'Improve security and reliability of the system',
          userStories: [],
          priority: 0,
          businessValue: { score: 70, rationale: 'Essential', metrics: [] },
          technicalComplexity: 'medium',
          status: 'proposed'
        },
        {
          id: 'feature-ai',
          initiativeId: 'init-1',
          name: 'AI-Powered Feature',
          description: 'Innovative AI capabilities for users',
          userStories: [],
          priority: 0,
          businessValue: { score: 80, rationale: 'Differentiator', metrics: [] },
          technicalComplexity: 'high',
          status: 'proposed'
        }
      ];

      const criteria: PrioritizationCriteria = {
        method: 'kano'
      };

      const results = await engine.prioritizeFeatures(kanoFeatures, criteria);

      // Security features should be categorized as basic
      const securityFeature = results.find(r => r.featureId === 'feature-security');
      expect(securityFeature!.breakdown.category).toBe('basic');

      // AI features should be categorized as excitement
      const aiFeature = results.find(r => r.featureId === 'feature-ai');
      expect(aiFeature!.breakdown.category).toBe('excitement');
      expect(aiFeature!.score).toBe(100); // Excitement features get highest score
    });
  });

  describe('Custom Weighted Scoring', () => {
    it('should prioritize features using custom weights', async () => {
      const criteria: PrioritizationCriteria = {
        method: 'custom',
        weights: {
          businessValue: 3,
          userImpact: 2,
          strategicAlignment: 2,
          technicalFeasibility: 1,
          risk: 1
        }
      };

      const results = await engine.prioritizeFeatures(testFeatures, criteria);

      expect(results).toHaveLength(testFeatures.length);
      
      results.forEach(result => {
        expect(result.method).toBe('custom');
        expect(result.breakdown.weights).toEqual(criteria.weights);
        expect(result.breakdown.businessValue).toBeGreaterThanOrEqual(0);
        expect(result.breakdown.businessValue).toBeLessThanOrEqual(100);
      });

      // Features with high business value should rank higher given the weights
      const highValueFeature = results.find(r => r.featureId === 'feature-1');
      const lowValueFeature = results.find(r => r.featureId === 'feature-4');
      
      expect(highValueFeature!.rank).toBeLessThan(lowValueFeature!.rank);
    });

    it('should handle missing weights gracefully', async () => {
      const criteria: PrioritizationCriteria = {
        method: 'custom'
        // No weights provided - should use defaults or throw error
      };

      // This should throw an error or use default weights
      await expect(engine.prioritizeFeatures(testFeatures, criteria))
        .rejects.toThrow();
    });
  });

  describe('Initiative Prioritization', () => {
    it('should prioritize initiatives', async () => {
      const initiatives: Initiative[] = [
        {
          id: 'init-1',
          themeId: 'theme-1',
          title: 'High Value Initiative',
          description: 'Strategic initiative with high impact',
          features: [],
          epicIds: [],
          value: {
            userImpact: 'high',
            revenueImpact: 1000000,
            costSavings: 500000,
            strategicValue: 9,
            customerSatisfaction: 20
          },
          effort: {
            developmentWeeks: 20,
            designWeeks: 5,
            qaWeeks: 5,
            confidence: 'high'
          },
          risks: [],
          dependencies: [],
          status: 'validated'
        },
        {
          id: 'init-2',
          themeId: 'theme-1',
          title: 'Low Value Initiative',
          description: 'Minor improvement initiative',
          features: [],
          epicIds: [],
          value: {
            userImpact: 'low',
            revenueImpact: 50000,
            costSavings: 10000,
            strategicValue: 3,
            customerSatisfaction: 2
          },
          effort: {
            developmentWeeks: 4,
            designWeeks: 1,
            qaWeeks: 1,
            confidence: 'medium'
          },
          risks: [],
          dependencies: [],
          status: 'ideation'
        }
      ];

      const criteria: PrioritizationCriteria = {
        method: 'value-effort'
      };

      const results = await engine.prioritizeInitiatives(initiatives, criteria);

      expect(results).toHaveLength(initiatives.length);
      
      // High value initiative should rank higher despite higher effort
      const highValue = results.find(r => r.featureId === 'init-1');
      const lowValue = results.find(r => r.featureId === 'init-2');
      
      // Since the low value initiative has much lower effort, it might rank higher
      // Just check that both are ranked
      expect(highValue!.rank).toBeGreaterThanOrEqual(1);
      expect(highValue!.rank).toBeLessThanOrEqual(2);
      expect(lowValue!.rank).toBeGreaterThanOrEqual(1);
      expect(lowValue!.rank).toBeLessThanOrEqual(2);
    });
  });

  describe('Edge Cases', () => {
    it('should handle empty feature list', async () => {
      const criteria: PrioritizationCriteria = {
        method: 'rice'
      };

      const results = await engine.prioritizeFeatures([], criteria);
      expect(results).toEqual([]);
    });

    it('should handle single feature', async () => {
      const criteria: PrioritizationCriteria = {
        method: 'rice'
      };

      const results = await engine.prioritizeFeatures([testFeatures[0]], criteria);
      expect(results).toHaveLength(1);
      expect(results[0].rank).toBe(1);
    });

    it('should throw error for unknown method', async () => {
      const criteria: PrioritizationCriteria = {
        method: 'unknown-method' as any
      };

      await expect(engine.prioritizeFeatures(testFeatures, criteria))
        .rejects.toThrow('Unknown prioritization method');
    });
  });
});