/**
 * Real-World Tests for Progress Calculator
 *
 * Tests critical scenarios that occur in production:
 * - Progress calculation accuracy across different course structures
 * - Level 2 topic filtering (business rule: only level 2 counts for progress)
 * - Edge cases (empty courses, null values, division by zero)
 * - Percentage rounding consistency for SCORM reporting
 * - Real course scenarios (test-full, test-quiz, test-lessons)
 */

import {
  countLevel2Topics,
  countCompletedLevel2Topics,
  calculateLessonProgress,
  calculateLessonPercentage,
  calculateCourseProgress,
  calculateTotalChapters,
} from '../progressCalculator.js';

// Mock the stateManager module
jest.mock('../../stateManager.js', () => ({
  isLessonFinished: jest.fn((topicId) => {
    // Mock implementation: topics with IDs containing 'completed' are finished
    return topicId.includes('completed');
  }),
}));

describe('Progress Calculator - Real-World Scenarios', () => {
  describe('Scenario 1: Level 2 Topic Filtering (Business Rule)', () => {
    test('should count only level 2 topics (excludes level 3 sub-topics)', () => {
      // Setup: Lesson with mixed topic levels
      // Why: Level 3 are sub-sections, not standalone lessons for progress
      const lesson = {
        children: [
          { id: 'topic1', level: 2 },  // Counts
          { id: 'topic2', level: 2 },  // Counts
          { id: 'topic3', level: 3 },  // Excluded (sub-topic)
          { id: 'topic4', level: 1 },  // Counts (level 1 is also counted as <= 2)
        ],
      };

      // Act & Assert: Should count levels 1 and 2, exclude level 3
      expect(countLevel2Topics(lesson)).toBe(3);
    });

    test('should exclude topics without level property', () => {
      // Setup: Malformed data (defensive programming)
      const lesson = {
        children: [
          { id: 'topic1', level: 2 },
          { id: 'topic2' }, // Missing level property
          { id: 'topic3', level: 2 },
        ],
      };

      // Act & Assert: Should safely handle missing properties
      expect(countLevel2Topics(lesson)).toBe(2);
    });

    test('should count completed topics with same level filtering', () => {
      // Setup: Mix of completed/incomplete at different levels
      const lesson = {
        children: [
          { id: 'completed-topic1', level: 2 }, // Completed, counts
          { id: 'topic2', level: 2 },           // Not completed
          { id: 'completed-topic3', level: 3 }, // Completed but excluded (level 3)
          { id: 'completed-topic4', level: 1 }, // Completed, counts
        ],
      };

      // Act & Assert: Only completed level 1-2 topics
      expect(countCompletedLevel2Topics(lesson)).toBe(2);
    });

    test('should exclude level 3 from course-wide progress calculation', () => {
      // Setup: Real-world scenario - course with sub-topics
      // Why: Including level 3 would dilute progress percentages
      const courses = [
        {
          children: [
            { id: 'completed-topic1', level: 2 },
            { id: 'completed-topic2', level: 3 }, // Excluded
            { id: 'topic3', level: 2 },
          ],
        },
      ];

      // Act
      const result = calculateCourseProgress(courses);

      // Assert: Progress based on level 2 only
      expect(result.totalTopics).toBe(2);
      expect(result.completedTopics).toBe(1);
      expect(result.progress).toBe(0.5);
      expect(result.percentage).toBe(50);
    });
  });

  describe('Scenario 2: Course Progress Calculation', () => {
    test('should aggregate progress across multiple lessons', () => {
      // Setup: Multi-lesson course (typical structure)
      const courses = [
        {
          children: [
            { id: 'completed-topic1', level: 2 },
            { id: 'topic2', level: 2 },
          ],
        },
        {
          children: [
            { id: 'completed-topic3', level: 2 },
            { id: 'completed-topic4', level: 2 },
            { id: 'topic5', level: 2 },
          ],
        },
      ];

      // Act
      const result = calculateCourseProgress(courses);

      // Assert: 3 completed out of 5 total = 60%
      expect(result.totalTopics).toBe(5);
      expect(result.completedTopics).toBe(3);
      expect(result.progress).toBe(0.6);
      expect(result.percentage).toBe(60);
    });

    test('should calculate lesson progress as decimal (0.0 to 1.0)', () => {
      // Setup: Half-complete lesson
      const lesson = {
        children: [
          { id: 'completed-topic1', level: 2 },
          { id: 'topic2', level: 2 },
          { id: 'completed-topic3', level: 2 },
          { id: 'topic4', level: 2 },
        ],
      };

      // Act & Assert: Returns decimal for SCORM reporting
      expect(calculateLessonProgress(lesson)).toBe(0.5);
    });

    test('should calculate total chapter count across all lessons', () => {
      // Setup: Multiple lessons with different topic counts
      const courses = [
        {
          children: [
            { id: 'topic1', level: 2 },
            { id: 'topic2', level: 2 },
          ],
        },
        {
          children: [
            { id: 'topic3', level: 2 },
            { id: 'topic4', level: 3 }, // Excluded
          ],
        },
      ];

      // Act & Assert: Total level 2 topics = 3
      expect(calculateTotalChapters(courses)).toBe(3);
    });
  });

  describe('Scenario 3: Percentage Rounding for SCORM', () => {
    test('should round percentage correctly (1/3 = 33%)', () => {
      // Setup: Progress that doesn't divide evenly
      // Why: SCORM gradebooks display integers, rounding matters
      const lesson = {
        children: [
          { id: 'completed-topic1', level: 2 },
          { id: 'topic2', level: 2 },
          { id: 'topic3', level: 2 },
        ],
      };

      // Act & Assert: 0.333... → 33% (not 34%)
      expect(calculateLessonPercentage(lesson)).toBe(33);
    });

    test('should round percentage correctly (2/3 = 67%)', () => {
      // Setup: Another rounding edge case
      const lesson = {
        children: [
          { id: 'completed-topic1', level: 2 },
          { id: 'completed-topic2', level: 2 },
          { id: 'topic3', level: 2 },
        ],
      };

      // Act & Assert: 0.666... → 67% (not 66%)
      expect(calculateLessonPercentage(lesson)).toBe(67);
    });

    test('should return 100% for fully completed lesson', () => {
      // Setup: All topics done
      const lesson = {
        children: [
          { id: 'completed-topic1', level: 2 },
          { id: 'completed-topic2', level: 2 },
        ],
      };

      // Act & Assert
      expect(calculateLessonPercentage(lesson)).toBe(100);
      expect(calculateLessonProgress(lesson)).toBe(1.0);
    });

    test('should return 0% for lesson with no progress', () => {
      // Setup: Nothing completed
      const lesson = {
        children: [
          { id: 'topic1', level: 2 },
          { id: 'topic2', level: 2 },
        ],
      };

      // Act & Assert
      expect(calculateLessonPercentage(lesson)).toBe(0);
      expect(calculateLessonProgress(lesson)).toBe(0);
    });
  });

  describe('Scenario 4: Edge Cases and Safety', () => {
    test('should handle empty lesson (0 topics)', () => {
      // Setup: Edge case - lesson with no topics
      // Why: Prevents division by zero
      expect(countLevel2Topics({})).toBe(0);
      expect(countLevel2Topics({ children: [] })).toBe(0);
      expect(calculateLessonProgress({})).toBe(0);
      expect(calculateLessonProgress({ children: [] })).toBe(0);
      expect(calculateLessonPercentage({})).toBe(0);
    });

    test('should handle null or undefined lesson gracefully', () => {
      // Setup: Defensive programming for null/undefined
      // Why: Prevents crashes in production
      expect(countLevel2Topics(null)).toBe(0);
      expect(countLevel2Topics(undefined)).toBe(0);
      expect(countCompletedLevel2Topics(null)).toBe(0);
      expect(countCompletedLevel2Topics(undefined)).toBe(0);
    });

    test('should handle empty course array', () => {
      // Setup: No courses loaded yet
      const emptyResult = {
        totalTopics: 0,
        completedTopics: 0,
        progress: 0,
        percentage: 0,
      };

      // Act & Assert: Safe defaults
      expect(calculateCourseProgress([])).toEqual(emptyResult);
      expect(calculateCourseProgress(null)).toEqual(emptyResult);
      expect(calculateCourseProgress(undefined)).toEqual(emptyResult);
      expect(calculateTotalChapters([])).toBe(0);
      expect(calculateTotalChapters(null)).toBe(0);
      expect(calculateTotalChapters(undefined)).toBe(0);
    });

    test('should handle courses with only level 3 topics (no level 2)', () => {
      // Setup: Courses with only sub-topics (level 3) that don't count toward progress
      // Why: Tests ternary false branch on line 80 (totalTopics === 0 but no early return)

      const courses = [
        {
          title: 'Lesson with only sub-topics',
          children: [
            { id: 'topic1', level: 3, title: 'Sub-topic 1' },
            { id: 'topic2', level: 3, title: 'Sub-topic 2' },
          ],
        },
        {
          title: 'Empty lesson',
          children: [],
        },
      ];

      // Act
      const result = calculateCourseProgress(courses);

      // Assert: Should safely return 0 progress without division by zero
      expect(result.totalTopics).toBe(0);
      expect(result.completedTopics).toBe(0);
      expect(result.progress).toBe(0); // Exercises ternary false branch (line 80)
      expect(result.percentage).toBe(0);
    });

    test('should handle lessons with missing children property', () => {
      // Setup: Malformed course data
      const courses = [
        {
          children: [
            { id: 'topic1', level: 2 },
          ],
        },
        {}, // Missing children
        {
          children: [],
        },
      ];

      // Act & Assert: Should not crash
      expect(calculateTotalChapters(courses)).toBe(1);
      expect(calculateCourseProgress(courses).totalTopics).toBe(1);
    });
  });

  describe('Scenario 5: Real-World Course Structures', () => {
    test('test-full course structure (3 content + 1 quiz = 4 modules)', () => {
      // Setup: Simulates test-full course
      // 4 modules: Intro, Features, Summary, Quiz
      const courses = [
        { children: [{ id: 'completed-1', level: 2 }] },              // Module 1
        { children: [{ id: 'completed-2', level: 2 }] },              // Module 2
        { children: [{ id: 'completed-3', level: 2 }] },              // Module 3
        { children: [{ id: 'quiz', level: 2 }] },                     // Module 4 (Quiz)
      ];

      // Act
      const result = calculateCourseProgress(courses);

      // Assert: 3 of 4 completed = 75%
      expect(result.totalTopics).toBe(4);
      expect(result.completedTopics).toBe(3);
      expect(result.progress).toBe(0.75);
      expect(result.percentage).toBe(75);
    });

    test('test-quiz course structure (quiz-only, 1 module)', () => {
      // Setup: Quiz-only course (no content modules)
      const courses = [
        { children: [{ id: 'quiz', level: 2 }] }, // Only quiz
      ];

      // Act - Before quiz
      const beforeQuiz = calculateCourseProgress(courses);
      expect(beforeQuiz.totalTopics).toBe(1);
      expect(beforeQuiz.completedTopics).toBe(0);
      expect(beforeQuiz.progress).toBe(0);

      // After quiz
      const coursesAfter = [
        { children: [{ id: 'completed-quiz', level: 2 }] },
      ];
      const afterQuiz = calculateCourseProgress(coursesAfter);
      expect(afterQuiz.progress).toBe(1.0);
      expect(afterQuiz.percentage).toBe(100);
    });

    test('test-lessons course structure (3 content modules, no quiz)', () => {
      // Setup: Content-only course (no quiz requirement)
      const courses = [
        { children: [{ id: 'completed-1', level: 2 }] },  // Module 1 done
        { children: [{ id: 'completed-2', level: 2 }] },  // Module 2 done
        { children: [{ id: 'topic-3', level: 2 }] },      // Module 3 not done
      ];

      // Act
      const result = calculateCourseProgress(courses);

      // Assert: 2 of 3 = 67%
      expect(result.totalTopics).toBe(3);
      expect(result.completedTopics).toBe(2);
      expect(result.progress).toBeCloseTo(0.67, 2);
      expect(result.percentage).toBe(67);
    });

    test('complex course with multiple topics per module', () => {
      // Setup: More realistic course structure
      // Module 1: 3 topics, Module 2: 2 topics, Module 3: 1 topic
      const courses = [
        {
          children: [
            { id: 'completed-1-1', level: 2 },
            { id: 'completed-1-2', level: 2 },
            { id: 'topic-1-3', level: 2 },
          ],
        },
        {
          children: [
            { id: 'completed-2-1', level: 2 },
            { id: 'topic-2-2', level: 2 },
          ],
        },
        {
          children: [
            { id: 'topic-3-1', level: 2 },
          ],
        },
      ];

      // Act
      const result = calculateCourseProgress(courses);

      // Assert: 3 completed of 6 total = 50%
      expect(result.totalTopics).toBe(6);
      expect(result.completedTopics).toBe(3);
      expect(result.progress).toBe(0.5);
      expect(result.percentage).toBe(50);
    });
  });

  describe('Scenario 6: Completion States (0% and 100%)', () => {
    test('should handle 0% completion (nothing started)', () => {
      // Setup: Fresh course
      const courses = [
        {
          children: [
            { id: 'topic1', level: 2 },
            { id: 'topic2', level: 2 },
          ],
        },
      ];

      // Act
      const result = calculateCourseProgress(courses);

      // Assert
      expect(result.totalTopics).toBe(2);
      expect(result.completedTopics).toBe(0);
      expect(result.progress).toBe(0);
      expect(result.percentage).toBe(0);
    });

    test('should handle 100% completion (everything done)', () => {
      // Setup: Fully completed course
      const courses = [
        {
          children: [
            { id: 'completed-topic1', level: 2 },
            { id: 'completed-topic2', level: 2 },
          ],
        },
      ];

      // Act
      const result = calculateCourseProgress(courses);

      // Assert
      expect(result.totalTopics).toBe(2);
      expect(result.completedTopics).toBe(2);
      expect(result.progress).toBe(1.0);
      expect(result.percentage).toBe(100);
    });
  });
});
