import type { AlgorithmDefinition, IntervalState, IntervalListState } from '@/lib/visualizations/types';

/**
 * Merge Intervals (LeetCode 56)
 *
 * Input: intervals = [[1,3], [2,6], [8,10], [15,18]]
 * Output: [[1,6], [8,10], [15,18]]
 *
 * Sort by start time, then merge overlapping intervals in one pass.
 */

// Helper to create input intervals state
function createInputIntervals(
  intervals: [number, number][],
  currentIndex?: number,
  processedIndices: number[] = []
): IntervalListState {
  return {
    id: 'input',
    label: 'Input Intervals (sorted)',
    minValue: 0,
    maxValue: 20,
    intervals: intervals.map(([start, end], index) => ({
      id: `input-${index}`,
      start,
      end,
      label: `[${start},${end}]`,
      state: (index === currentIndex
        ? 'highlighted'
        : processedIndices.includes(index)
        ? 'dimmed'
        : 'normal') as IntervalState['state'],
    })),
  };
}

// Helper to create result intervals state
function createResultIntervals(
  intervals: [number, number][],
  highlightLast = false,
  mergingLast = false
): IntervalListState {
  return {
    id: 'result',
    label: 'Merged Result',
    minValue: 0,
    maxValue: 20,
    intervals: intervals.map(([start, end], index) => ({
      id: `result-${index}`,
      start,
      end,
      label: `[${start},${end}]`,
      state: (index === intervals.length - 1
        ? mergingLast
          ? 'merging'
          : highlightLast
          ? 'highlighted'
          : 'merged'
        : 'merged') as IntervalState['state'],
    })),
  };
}

export const mergeIntervalsAlgorithm: AlgorithmDefinition = {
  id: 'merge-intervals',
  title: 'Merge Intervals',
  slug: 'merge-intervals',
  pattern: {
    name: 'Intervals',
    description:
      'Sort intervals by start time, then process them linearly to merge overlapping ranges.',
  },
  problemStatement:
    'Given an array of intervals where intervals[i] = [start_i, end_i], merge all overlapping intervals and return an array of non-overlapping intervals.',
  intuition:
    'After sorting by start time, overlapping intervals become adjacent. Two sorted intervals [a,b] and [c,d] overlap if c <= b. When they overlap, merge them by extending the end to max(b, d).',
  code: {
    language: 'python',
    code: `def merge(intervals: list[list[int]]) -> list[list[int]]:
    if len(intervals) <= 1:
        return intervals

    # Sort by start time
    intervals.sort(key=lambda x: x[0])

    merged = [intervals[0]]

    for current in intervals[1:]:
        last = merged[-1]

        if current[0] <= last[1]:  # Overlap
            last[1] = max(last[1], current[1])
        else:
            merged.append(current)

    return merged`,
  },
  initialExample: {
    input: { intervals: [[1, 3], [2, 6], [8, 10], [15, 18]] },
    expected: [[1, 6], [8, 10], [15, 18]],
  },
  steps: [
    // ==========================================
    // Phase 1: Problem (2 steps)
    // ==========================================
    {
      id: 'problem-1',
      phase: 'problem',
      explanation:
        'Given intervals [[1,3], [2,6], [8,10], [15,18]], merge all overlapping intervals. Notice [1,3] and [2,6] share the range [2,3].',
      dataState: {
        arrays: [],
        pointers: [],
        variables: [],
        calculations: [],
        intervals: [
          createInputIntervals([[1, 3], [2, 6], [8, 10], [15, 18]]),
        ],
      },
    },
    {
      id: 'problem-2',
      phase: 'problem',
      explanation:
        'Brute force: Compare every pair of intervals, O(n^2). But if intervals are sorted, we can detect overlaps in a single pass!',
      dataState: {
        arrays: [],
        pointers: [],
        variables: [
          { id: 'brute', name: 'brute force', value: 'O(n^2)' },
          { id: 'optimal', name: 'optimal', value: 'O(n log n)' },
        ],
        calculations: [],
        intervals: [
          createInputIntervals([[1, 3], [2, 6], [8, 10], [15, 18]]),
        ],
      },
    },

    // ==========================================
    // Phase 2: Intuition (3 steps)
    // ==========================================
    {
      id: 'intuition-1',
      phase: 'intuition',
      explanation:
        'Key insight: When sorted by start time, overlapping intervals are always adjacent. No need to compare distant intervals!',
      dataState: {
        arrays: [],
        pointers: [],
        variables: [],
        calculations: [],
        intervals: [
          createInputIntervals([[1, 3], [2, 6], [8, 10], [15, 18]]),
        ],
      },
    },
    {
      id: 'intuition-2',
      phase: 'intuition',
      explanation:
        'Two intervals [a,b] and [c,d] (with a <= c) overlap when c <= b. In our example, [1,3] and [2,6]: 2 <= 3, so they overlap!',
      dataState: {
        arrays: [],
        pointers: [],
        variables: [
          { id: 'overlap', name: 'overlap condition', value: 'c <= b' },
        ],
        calculations: [],
        intervals: [
          createInputIntervals([[1, 3], [2, 6], [8, 10], [15, 18]], 0),
        ],
      },
    },
    {
      id: 'intuition-3',
      phase: 'intuition',
      explanation:
        'To merge, extend the end to max(b, d). [1,3] + [2,6] = [1, max(3,6)] = [1,6]. This covers both original ranges.',
      dataState: {
        arrays: [],
        pointers: [],
        variables: [
          { id: 'merge', name: 'merge formula', value: '[a, max(b,d)]' },
        ],
        calculations: [],
        intervals: [
          createInputIntervals([[1, 3], [2, 6], [8, 10], [15, 18]], 1),
        ],
      },
    },

    // ==========================================
    // Phase 3: Pattern (2 steps)
    // ==========================================
    {
      id: 'pattern-1',
      phase: 'pattern',
      explanation:
        'Intervals pattern: Sort by start time first. This transforms O(n^2) pair comparisons into O(n) sequential checks.',
      dataState: {
        arrays: [],
        pointers: [],
        variables: [
          { id: 'pattern', name: 'pattern', value: 'sort + linear scan' },
        ],
        calculations: [],
        intervals: [
          createInputIntervals([[1, 3], [2, 6], [8, 10], [15, 18]]),
        ],
      },
    },
    {
      id: 'pattern-2',
      phase: 'pattern',
      explanation:
        'Build result incrementally: For each interval, either merge with the last result interval (if overlapping) or add as new.',
      dataState: {
        arrays: [],
        pointers: [],
        variables: [
          { id: 'time', name: 'Time', value: 'O(n log n)' },
          { id: 'space', name: 'Space', value: 'O(n)' },
        ],
        calculations: [],
        intervals: [
          createInputIntervals([[1, 3], [2, 6], [8, 10], [15, 18]]),
        ],
      },
    },

    // ==========================================
    // Phase 4: Code (3 steps)
    // ==========================================
    {
      id: 'code-1',
      phase: 'code',
      explanation:
        'Sort intervals by start time. Our input is already sorted: [[1,3], [2,6], [8,10], [15,18]].',
      codeLine: 6,
      codeHighlightLines: [5, 6],
      dataState: {
        arrays: [],
        pointers: [],
        variables: [],
        calculations: [],
        intervals: [
          createInputIntervals([[1, 3], [2, 6], [8, 10], [15, 18]]),
        ],
      },
    },
    {
      id: 'code-2',
      phase: 'code',
      explanation:
        'Initialize merged list with first interval. This becomes our "current" merged interval to potentially extend.',
      codeLine: 8,
      codeHighlightLines: [8],
      dataState: {
        arrays: [],
        pointers: [],
        variables: [],
        calculations: [],
        intervals: [
          createInputIntervals([[1, 3], [2, 6], [8, 10], [15, 18]], 0),
          createResultIntervals([[1, 3]], true),
        ],
      },
    },
    {
      id: 'code-3',
      phase: 'code',
      explanation:
        'For each remaining interval: if it overlaps with the last merged (current[0] <= last[1]), extend. Otherwise, add as new interval.',
      codeLine: 13,
      codeHighlightLines: [10, 11, 13, 14, 15, 16],
      dataState: {
        arrays: [],
        pointers: [],
        variables: [],
        calculations: [],
        intervals: [
          createInputIntervals([[1, 3], [2, 6], [8, 10], [15, 18]]),
          createResultIntervals([[1, 3]]),
        ],
      },
    },

    // ==========================================
    // Phase 5: Execution (~10 steps)
    // ==========================================
    {
      id: 'exec-1',
      phase: 'execution',
      explanation:
        'Initialize: Add first interval [1,3] to merged result.',
      codeLine: 8,
      dataState: {
        arrays: [],
        pointers: [],
        variables: [
          { id: 'merged', name: 'merged', value: '[[1,3]]' },
        ],
        calculations: [],
        intervals: [
          createInputIntervals([[1, 3], [2, 6], [8, 10], [15, 18]], 0, []),
          createResultIntervals([[1, 3]], true),
        ],
      },
    },
    {
      id: 'exec-2',
      phase: 'execution',
      explanation:
        'Process [2,6]: Check overlap with last merged [1,3]. Is 2 <= 3? Yes! They overlap.',
      codeLine: 13,
      decision: {
        question: 'Does current[0] <= last[1]?',
        answer: '2 <= 3 = true',
        action: 'Intervals overlap, merge them',
      },
      dataState: {
        arrays: [],
        pointers: [],
        variables: [
          { id: 'current', name: 'current', value: '[2,6]' },
          { id: 'last', name: 'last', value: '[1,3]' },
        ],
        calculations: [],
        intervals: [
          createInputIntervals([[1, 3], [2, 6], [8, 10], [15, 18]], 1, [0]),
          createResultIntervals([[1, 3]], false, true),
        ],
      },
    },
    {
      id: 'exec-3',
      phase: 'execution',
      explanation:
        'Merge: Extend last interval\'s end to max(3, 6) = 6. Result: [1,6].',
      codeLine: 14,
      dataState: {
        arrays: [],
        pointers: [],
        variables: [
          { id: 'merged', name: 'merged', value: '[[1,6]]' },
          { id: 'calc', name: 'new end', value: 'max(3,6) = 6' },
        ],
        calculations: [],
        intervals: [
          createInputIntervals([[1, 3], [2, 6], [8, 10], [15, 18]], 1, [0]),
          createResultIntervals([[1, 6]], true),
        ],
      },
    },
    {
      id: 'exec-4',
      phase: 'execution',
      explanation:
        'Process [8,10]: Check overlap with last merged [1,6]. Is 8 <= 6? No! No overlap.',
      codeLine: 13,
      decision: {
        question: 'Does current[0] <= last[1]?',
        answer: '8 <= 6 = false',
        action: 'No overlap, add as new interval',
      },
      dataState: {
        arrays: [],
        pointers: [],
        variables: [
          { id: 'current', name: 'current', value: '[8,10]' },
          { id: 'last', name: 'last', value: '[1,6]' },
        ],
        calculations: [],
        intervals: [
          createInputIntervals([[1, 3], [2, 6], [8, 10], [15, 18]], 2, [0, 1]),
          createResultIntervals([[1, 6]]),
        ],
      },
    },
    {
      id: 'exec-5',
      phase: 'execution',
      explanation:
        'Add [8,10] as a new interval in the result. Gap between [1,6] and [8,10] is preserved.',
      codeLine: 16,
      dataState: {
        arrays: [],
        pointers: [],
        variables: [
          { id: 'merged', name: 'merged', value: '[[1,6], [8,10]]' },
        ],
        calculations: [],
        intervals: [
          createInputIntervals([[1, 3], [2, 6], [8, 10], [15, 18]], 2, [0, 1]),
          createResultIntervals([[1, 6], [8, 10]], true),
        ],
      },
    },
    {
      id: 'exec-6',
      phase: 'execution',
      explanation:
        'Process [15,18]: Check overlap with last merged [8,10]. Is 15 <= 10? No! No overlap.',
      codeLine: 13,
      decision: {
        question: 'Does current[0] <= last[1]?',
        answer: '15 <= 10 = false',
        action: 'No overlap, add as new interval',
      },
      dataState: {
        arrays: [],
        pointers: [],
        variables: [
          { id: 'current', name: 'current', value: '[15,18]' },
          { id: 'last', name: 'last', value: '[8,10]' },
        ],
        calculations: [],
        intervals: [
          createInputIntervals([[1, 3], [2, 6], [8, 10], [15, 18]], 3, [0, 1, 2]),
          createResultIntervals([[1, 6], [8, 10]]),
        ],
      },
    },
    {
      id: 'exec-7',
      phase: 'execution',
      explanation:
        'Add [15,18] as a new interval. Result now has 3 non-overlapping intervals.',
      codeLine: 16,
      dataState: {
        arrays: [],
        pointers: [],
        variables: [
          { id: 'merged', name: 'merged', value: '[[1,6], [8,10], [15,18]]' },
        ],
        calculations: [],
        intervals: [
          createInputIntervals([[1, 3], [2, 6], [8, 10], [15, 18]], 3, [0, 1, 2]),
          createResultIntervals([[1, 6], [8, 10], [15, 18]], true),
        ],
      },
    },
    {
      id: 'exec-8',
      phase: 'execution',
      explanation:
        'Done! Return merged = [[1,6], [8,10], [15,18]]. We merged 4 intervals into 3.',
      codeLine: 18,
      dataState: {
        arrays: [],
        pointers: [],
        variables: [
          { id: 'result', name: 'result', value: '[[1,6], [8,10], [15,18]]' },
        ],
        calculations: [],
        intervals: [
          createInputIntervals([[1, 3], [2, 6], [8, 10], [15, 18]], undefined, [0, 1, 2, 3]),
          createResultIntervals([[1, 6], [8, 10], [15, 18]]),
        ],
      },
    },
    {
      id: 'exec-9',
      phase: 'execution',
      explanation:
        'Time: O(n log n) for sorting + O(n) for merging. Space: O(n) for result. Sorting is the bottleneck.',
      codeLine: 18,
      decision: {
        question: 'Why sort first?',
        answer: 'Sorted intervals make overlap detection O(1) per pair',
        action: 'Only need to compare adjacent intervals after sorting',
      },
      dataState: {
        arrays: [],
        pointers: [],
        variables: [
          { id: 'result', name: 'result', value: '[[1,6], [8,10], [15,18]]' },
          { id: 'time', name: 'Time', value: 'O(n log n)' },
          { id: 'space', name: 'Space', value: 'O(n)' },
        ],
        calculations: [],
        intervals: [
          createInputIntervals([[1, 3], [2, 6], [8, 10], [15, 18]], undefined, [0, 1, 2, 3]),
          createResultIntervals([[1, 6], [8, 10], [15, 18]]),
        ],
      },
    },
  ],
};
