import type {
  AlgorithmDefinition,
  ArrayElementState,
  BitState,
  BitManipulationState,
} from '@/lib/visualizations/types';

/**
 * Single Number - LeetCode 136
 *
 * Given a non-empty array where every element appears twice except for one,
 * find that single element.
 *
 * Example: nums = [2, 2, 1]
 * Output: 1
 *
 * Key insight: XOR all numbers together. Pairs cancel out (a ^ a = 0),
 * leaving only the single number.
 */

// Helper to convert number to binary string
function toBinary(n: number, width: number = 4): string {
  if (n < 0) {
    // Handle negative numbers with two's complement representation
    return (n >>> 0).toString(2).slice(-width);
  }
  return n.toString(2).padStart(width, '0');
}

// Helper to create array elements
function createArrayElements(
  nums: number[],
  highlightIndex?: number,
  processedIndices: number[] = []
): ArrayElementState[] {
  return nums.map((value, index) => ({
    value,
    index,
    state:
      index === highlightIndex
        ? 'highlighted'
        : processedIndices.includes(index)
          ? 'dimmed'
          : 'normal',
  }));
}

// Helper to create bit state
function createBit(
  id: string,
  value: number,
  state: BitState['state'],
  label?: string
): BitState {
  return {
    id,
    value,
    bits: toBinary(value),
    state,
    label,
  };
}

// Helper to create bit manipulation state
function createBitManip(
  id: string,
  operands: BitState[],
  operation?: BitManipulationState['operation'],
  result?: BitState,
  label?: string
): BitManipulationState {
  return { id, operands, operation, result, label };
}

// Input array
const NUMS = [2, 2, 1];

export const singleNumberAlgorithm: AlgorithmDefinition = {
  id: 'single-number',
  title: 'Single Number',
  slug: 'single-number',
  pattern: {
    name: 'Bit Manipulation',
    description:
      'Techniques using binary operations (AND, OR, XOR, NOT, shifts) to solve problems efficiently, often achieving O(1) space where other approaches need O(n).',
  },
  problemStatement:
    'Given a non-empty array of integers where every element appears twice except for one, find that single element. You must use O(n) time and O(1) space.',
  intuition:
    'XOR has two magical properties: a ^ a = 0 (any number XORed with itself is zero) and a ^ 0 = a (XORing with zero preserves the value). When you XOR all numbers together, every pair cancels out to zero, leaving only the single number.',
  code: {
    language: 'python',
    code: `def single_number(nums: list[int]) -> int:
    result = 0

    for num in nums:
        result ^= num  # XOR each number

    return result`,
  },
  initialExample: {
    input: { nums: [2, 2, 1] },
    expected: 1,
  },
  steps: [
    // ==========================================
    // Phase 1: Problem (2 steps)
    // ==========================================
    {
      id: 'problem-1',
      phase: 'problem',
      explanation:
        'We have an array where every element appears exactly twice, except for one element that appears only once. Our goal is to find this unique element.',
      dataState: {
        arrays: [
          {
            id: 'nums',
            label: 'nums',
            elements: createArrayElements(NUMS),
          },
        ],
        pointers: [],
        variables: [
          { id: 'nums', name: 'nums', value: '[2, 2, 1]' },
        ],
        calculations: [],
      },
    },
    {
      id: 'problem-2',
      phase: 'problem',
      explanation:
        'The constraint is key: we must solve this in O(n) time with O(1) extra space. This rules out hash maps and sorting. We need a clever approach.',
      dataState: {
        arrays: [
          {
            id: 'nums',
            label: 'nums',
            elements: createArrayElements(NUMS),
          },
        ],
        pointers: [],
        variables: [
          { id: 'time', name: 'Time', value: 'O(n)' },
          { id: 'space', name: 'Space', value: 'O(1)' },
        ],
        calculations: [],
      },
    },

    // ==========================================
    // Phase 2: Intuition - XOR Properties (4 steps)
    // ==========================================
    {
      id: 'intuition-1',
      phase: 'intuition',
      explanation:
        'Let\'s explore XOR (exclusive or). At the bit level: 0⊕0=0, 1⊕1=0, 0⊕1=1, 1⊕0=1. Same bits give 0, different bits give 1.',
      dataState: {
        arrays: [],
        pointers: [],
        variables: [
          { id: 'xor-rule', name: 'XOR Rule', value: 'same=0, different=1' },
        ],
        calculations: [],
        bitManipulation: [
          createBitManip(
            'xor-demo',
            [
              createBit('bit1', 0, 'comparing'),
              createBit('bit2', 0, 'comparing'),
            ],
            'XOR',
            createBit('res', 0, 'result'),
            'Bit XOR: 0 ⊕ 0 = 0'
          ),
        ],
      },
    },
    {
      id: 'intuition-2',
      phase: 'intuition',
      explanation:
        'Key property #1: a ⊕ a = 0. Any number XORed with itself equals zero. All the same bits cancel out!',
      dataState: {
        arrays: [],
        pointers: [],
        variables: [
          { id: 'property1', name: 'Property 1', value: 'a ⊕ a = 0' },
        ],
        calculations: [],
        bitManipulation: [
          createBitManip(
            'self-xor',
            [
              createBit('a1', 5, 'comparing', 'a'),
              createBit('a2', 5, 'comparing', 'a'),
            ],
            'XOR',
            createBit('zero', 0, 'cancelled', '0'),
            '5 ⊕ 5 = 0'
          ),
        ],
      },
    },
    {
      id: 'intuition-3',
      phase: 'intuition',
      explanation:
        'Key property #2: a ⊕ 0 = a. XORing any number with zero preserves it. Zero is the identity element for XOR.',
      dataState: {
        arrays: [],
        pointers: [],
        variables: [
          { id: 'property2', name: 'Property 2', value: 'a ⊕ 0 = a' },
        ],
        calculations: [],
        bitManipulation: [
          createBitManip(
            'zero-xor',
            [
              createBit('a', 5, 'highlighted', 'a'),
              createBit('zero', 0, 'normal', '0'),
            ],
            'XOR',
            createBit('result', 5, 'result', 'a'),
            '5 ⊕ 0 = 5'
          ),
        ],
      },
    },
    {
      id: 'intuition-4',
      phase: 'intuition',
      explanation:
        'The insight: if we XOR all numbers together, every pair (a ⊕ a) cancels to 0, leaving only the single number (single ⊕ 0 = single)!',
      dataState: {
        arrays: [
          {
            id: 'nums',
            label: 'nums',
            elements: createArrayElements(NUMS),
          },
        ],
        pointers: [],
        variables: [
          { id: 'insight', name: 'Insight', value: '2⊕2⊕1 = 0⊕1 = 1' },
        ],
        calculations: [],
      },
    },

    // ==========================================
    // Phase 3: Pattern (2 steps)
    // ==========================================
    {
      id: 'pattern-1',
      phase: 'pattern',
      explanation:
        'The XOR accumulator pattern: start with result=0, then XOR each element. Pairs vanish, the unique element remains.',
      dataState: {
        arrays: [],
        pointers: [],
        variables: [
          { id: 'pattern', name: 'Pattern', value: 'XOR Accumulator' },
          { id: 'formula', name: 'Formula', value: 'result ^= num' },
        ],
        calculations: [],
        bitManipulation: [
          createBitManip(
            'pattern-demo',
            [createBit('result', 0, 'highlighted', 'result')],
            undefined,
            undefined,
            'Initialize result = 0'
          ),
        ],
      },
    },
    {
      id: 'pattern-2',
      phase: 'pattern',
      explanation:
        'This pattern works because XOR is commutative (a⊕b = b⊕a) and associative ((a⊕b)⊕c = a⊕(b⊕c)). The order doesn\'t matter!',
      dataState: {
        arrays: [],
        pointers: [],
        variables: [
          { id: 'commutative', name: 'Commutative', value: 'a⊕b = b⊕a' },
          { id: 'associative', name: 'Associative', value: '(a⊕b)⊕c = a⊕(b⊕c)' },
        ],
        calculations: [],
      },
    },

    // ==========================================
    // Phase 4: Code (2 steps)
    // ==========================================
    {
      id: 'code-1',
      phase: 'code',
      explanation:
        'Initialize result to 0. Since a ⊕ 0 = a, starting with 0 means the first XOR just sets result to the first number.',
      codeLine: 2,
      codeHighlightLines: [1, 2],
      dataState: {
        arrays: [
          {
            id: 'nums',
            label: 'nums',
            elements: createArrayElements(NUMS),
          },
        ],
        pointers: [],
        variables: [{ id: 'result', name: 'result', value: 0 }],
        calculations: [],
      },
    },
    {
      id: 'code-2',
      phase: 'code',
      explanation:
        'Loop through each number and XOR it with result. The ^= operator is shorthand for result = result ^ num.',
      codeLine: 5,
      codeHighlightLines: [4, 5],
      dataState: {
        arrays: [
          {
            id: 'nums',
            label: 'nums',
            elements: createArrayElements(NUMS),
          },
        ],
        pointers: [],
        variables: [
          { id: 'result', name: 'result', value: 0 },
          { id: 'loop', name: 'loop', value: 'for num in nums' },
        ],
        calculations: [],
      },
    },

    // ==========================================
    // Phase 5: Execution - Process [2, 2, 1] (10 steps)
    // ==========================================
    {
      id: 'exec-init',
      phase: 'execution',
      explanation:
        'Initialize result = 0. In binary: 0000. We\'ll XOR each number from the array with this result.',
      codeLine: 2,
      dataState: {
        arrays: [
          {
            id: 'nums',
            label: 'nums',
            elements: createArrayElements(NUMS),
          },
        ],
        pointers: [],
        variables: [{ id: 'result', name: 'result', value: '0 (0000)' }],
        calculations: [],
        bitManipulation: [
          createBitManip(
            'init',
            [createBit('result', 0, 'highlighted', 'result')],
            undefined,
            undefined,
            'result = 0'
          ),
        ],
      },
    },
    {
      id: 'exec-1-setup',
      phase: 'execution',
      explanation:
        'Process first element: num = 2. In binary: 0010. We\'ll compute result ⊕ num = 0 ⊕ 2.',
      codeLine: 4,
      dataState: {
        arrays: [
          {
            id: 'nums',
            label: 'nums',
            elements: createArrayElements(NUMS, 0),
          },
        ],
        pointers: [{ id: 'i', name: 'i', index: 0, color: 'default' }],
        variables: [
          { id: 'result', name: 'result', value: '0 (0000)' },
          { id: 'num', name: 'num', value: '2 (0010)' },
        ],
        calculations: [],
        bitManipulation: [
          createBitManip(
            'xor-1',
            [
              createBit('result', 0, 'comparing', 'result'),
              createBit('num', 2, 'comparing', 'num'),
            ],
            'XOR',
            undefined,
            '0 ⊕ 2 = ?'
          ),
        ],
      },
    },
    {
      id: 'exec-1-result',
      phase: 'execution',
      explanation:
        'result = 0 ⊕ 2 = 2. In binary: 0000 ⊕ 0010 = 0010. Each bit: 0⊕0=0, 0⊕0=0, 0⊕1=1, 0⊕0=0.',
      codeLine: 5,
      dataState: {
        arrays: [
          {
            id: 'nums',
            label: 'nums',
            elements: createArrayElements(NUMS, 0, []),
          },
        ],
        pointers: [{ id: 'i', name: 'i', index: 0, color: 'default' }],
        variables: [
          { id: 'result', name: 'result', value: '2 (0010)', previousValue: '0' },
        ],
        calculations: [],
        bitManipulation: [
          createBitManip(
            'xor-1',
            [
              createBit('prev', 0, 'normal', 'prev'),
              createBit('num', 2, 'comparing', 'num'),
            ],
            'XOR',
            createBit('result', 2, 'result', 'result'),
            '0 ⊕ 2 = 2'
          ),
        ],
      },
    },
    {
      id: 'exec-2-setup',
      phase: 'execution',
      explanation:
        'Process second element: num = 2 again. We\'ll compute result ⊕ num = 2 ⊕ 2. Same number!',
      codeLine: 4,
      dataState: {
        arrays: [
          {
            id: 'nums',
            label: 'nums',
            elements: createArrayElements(NUMS, 1, [0]),
          },
        ],
        pointers: [{ id: 'i', name: 'i', index: 1, color: 'default' }],
        variables: [
          { id: 'result', name: 'result', value: '2 (0010)' },
          { id: 'num', name: 'num', value: '2 (0010)' },
        ],
        calculations: [],
        bitManipulation: [
          createBitManip(
            'xor-2',
            [
              createBit('result', 2, 'comparing', 'result'),
              createBit('num', 2, 'comparing', 'num'),
            ],
            'XOR',
            undefined,
            '2 ⊕ 2 = ?'
          ),
        ],
      },
    },
    {
      id: 'exec-2-result',
      phase: 'execution',
      explanation:
        'result = 2 ⊕ 2 = 0! The pair cancels out! In binary: 0010 ⊕ 0010 = 0000. Every bit is the same, so XOR gives all zeros.',
      codeLine: 5,
      dataState: {
        arrays: [
          {
            id: 'nums',
            label: 'nums',
            elements: createArrayElements(NUMS, 1, [0]),
          },
        ],
        pointers: [{ id: 'i', name: 'i', index: 1, color: 'default' }],
        variables: [
          { id: 'result', name: 'result', value: '0 (0000)', previousValue: '2' },
          { id: 'insight', name: 'insight', value: 'Pair cancelled!' },
        ],
        calculations: [],
        bitManipulation: [
          createBitManip(
            'xor-2',
            [
              createBit('prev', 2, 'cancelled', '2'),
              createBit('num', 2, 'cancelled', '2'),
            ],
            'XOR',
            createBit('result', 0, 'cancelled', '0'),
            '2 ⊕ 2 = 0 (cancelled!)'
          ),
        ],
      },
    },
    {
      id: 'exec-3-setup',
      phase: 'execution',
      explanation:
        'Process third element: num = 1. This is the single number! We\'ll compute result ⊕ num = 0 ⊕ 1.',
      codeLine: 4,
      dataState: {
        arrays: [
          {
            id: 'nums',
            label: 'nums',
            elements: createArrayElements(NUMS, 2, [0, 1]),
          },
        ],
        pointers: [{ id: 'i', name: 'i', index: 2, color: 'default' }],
        variables: [
          { id: 'result', name: 'result', value: '0 (0000)' },
          { id: 'num', name: 'num', value: '1 (0001)' },
        ],
        calculations: [],
        bitManipulation: [
          createBitManip(
            'xor-3',
            [
              createBit('result', 0, 'comparing', 'result'),
              createBit('num', 1, 'highlighted', 'num'),
            ],
            'XOR',
            undefined,
            '0 ⊕ 1 = ?'
          ),
        ],
      },
    },
    {
      id: 'exec-3-result',
      phase: 'execution',
      explanation:
        'result = 0 ⊕ 1 = 1! In binary: 0000 ⊕ 0001 = 0001. XORing with 0 preserves the value, so the single number emerges!',
      codeLine: 5,
      dataState: {
        arrays: [
          {
            id: 'nums',
            label: 'nums',
            elements: createArrayElements(NUMS, 2, [0, 1]),
          },
        ],
        pointers: [{ id: 'i', name: 'i', index: 2, color: 'default' }],
        variables: [
          { id: 'result', name: 'result', value: '1 (0001)', previousValue: '0' },
        ],
        calculations: [],
        bitManipulation: [
          createBitManip(
            'xor-3',
            [
              createBit('prev', 0, 'normal', '0'),
              createBit('num', 1, 'highlighted', '1'),
            ],
            'XOR',
            createBit('result', 1, 'result', 'result'),
            '0 ⊕ 1 = 1'
          ),
        ],
      },
    },
    {
      id: 'exec-done',
      phase: 'execution',
      explanation:
        'Loop complete! Return result = 1. The pair (2, 2) cancelled out, leaving only the single number: 1.',
      codeLine: 7,
      codeHighlightLines: [7],
      dataState: {
        arrays: [
          {
            id: 'nums',
            label: 'nums',
            elements: NUMS.map((value, index) => ({
              value,
              index,
              state: value === 1 ? 'success' : 'dimmed',
            } as ArrayElementState)),
          },
        ],
        pointers: [],
        variables: [
          { id: 'result', name: 'return', value: 1 },
        ],
        calculations: [],
        bitManipulation: [
          createBitManip(
            'final',
            [createBit('result', 1, 'result', 'result')],
            undefined,
            undefined,
            'Single Number: 1'
          ),
        ],
      },
    },
    {
      id: 'exec-summary',
      phase: 'execution',
      explanation:
        'Summary: 0 ⊕ 2 = 2, then 2 ⊕ 2 = 0 (pair cancelled!), then 0 ⊕ 1 = 1. The single number remains after all pairs cancel.',
      dataState: {
        arrays: [],
        pointers: [],
        variables: [
          { id: 'answer', name: 'Answer', value: 1 },
          { id: 'time', name: 'Time', value: 'O(n)' },
          { id: 'space', name: 'Space', value: 'O(1)' },
        ],
        calculations: [],
      },
    },
  ],
};
