import {
  calculateProgress,
  formatDate,
  formatDateTime,
  formatTime,
  sortByStepType,
  findFirstPendingIndex,
  generateProductCode,
  debounce,
  throttle,
  safeParseNumber,
  safeParseInt,
  groupProductsByNameAndWeight,
  calculateSummary,
} from '../../utils/helpers';
import { stepOrder } from '../../utils/constants';

describe('calculateProgress', () => {
  it('should calculate correct percentage', () => {
    expect(calculateProgress(50, 100)).toBe(50);
    expect(calculateProgress(25, 100)).toBe(25);
    expect(calculateProgress(0, 100)).toBe(0);
    expect(calculateProgress(100, 100)).toBe(100);
  });

  it('should handle edge cases', () => {
    expect(calculateProgress(0, 0)).toBe(0);
    expect(calculateProgress(50, 0)).toBe(0);
    expect(calculateProgress(150, 100)).toBe(100);
  });

  it('should round to integer', () => {
    expect(calculateProgress(33.3, 100)).toBe(33);
    expect(calculateProgress(66.6, 100)).toBe(67);
  });
});

describe('formatDate', () => {
  it('should format date correctly', () => {
    const date = '2024-01-15T10:30:00Z';
    const result = formatDate(date);
    expect(result).toMatch(/2024[/-]01[/-]15/);
  });

  it('should handle invalid date', () => {
    expect(formatDate('invalid')).toBe('Invalid Date');
    expect(formatDate('')).toBe('-');
  });
});

describe('formatDateTime', () => {
  it('should format datetime correctly', () => {
    const date = '2024-01-15T10:30:00Z';
    const result = formatDateTime(date);
    expect(result).toContain('2024');
    expect(result).toContain('10');
    expect(result).toContain('30');
  });

  it('should handle invalid date', () => {
    expect(formatDateTime('invalid')).toBe('Invalid Date');
  });
});

describe('formatTime', () => {
  it('should format time correctly', () => {
    const date = '2024-01-15T10:30:00Z';
    const result = formatTime(date);
    expect(result).toContain('10');
    expect(result).toContain('30');
  });

  it('should handle invalid date', () => {
    expect(formatTime('invalid')).toBe('Invalid Date');
  });
});

describe('sortByStepType', () => {
  it('should sort by step order', () => {
    const steps = [
      { stepType: 'machine_start' as const },
      { stepType: 'confirm' as const },
      { stepType: 'yarn_purchase' as const },
    ];
    const sorted = sortByStepType(steps);
    expect(sorted[0].stepType).toBe('confirm');
    expect(sorted[1].stepType).toBe('yarn_purchase');
    expect(sorted[2].stepType).toBe('machine_start');
  });

  it('should handle unknown step types', () => {
    const steps = [
      { stepType: 'unknown' as unknown as import('../../types').StepType },
      { stepType: 'confirm' },
    ];
    const sorted = sortByStepType(steps as { stepType: import('../../types').StepType }[]);
    expect(sorted[0].stepType).toBe('confirm');
    expect(sorted[1].stepType).toBe('unknown');
  });

  it('should handle empty array', () => {
    expect(sortByStepType([])).toEqual([]);
  });
});

describe('findFirstPendingIndex', () => {
  it('should find first pending index', () => {
    const steps = [
      { status: 'completed' as import('../../types').StepStatus },
      { status: 'completed' as import('../../types').StepStatus },
      { status: 'pending' as import('../../types').StepStatus },
      { status: 'pending' as import('../../types').StepStatus },
    ];
    expect(findFirstPendingIndex(steps)).toBe(2);
  });

  it('should return -1 when no pending', () => {
    const steps = [
      { status: 'completed' as import('../../types').StepStatus },
      { status: 'completed' as import('../../types').StepStatus },
    ];
    expect(findFirstPendingIndex(steps)).toBe(-1);
  });

  it('should return 0 when first is pending', () => {
    const steps = [
      { status: 'pending' as import('../../types').StepStatus },
      { status: 'completed' as import('../../types').StepStatus },
    ];
    expect(findFirstPendingIndex(steps)).toBe(0);
  });

  it('should handle empty array', () => {
    expect(findFirstPendingIndex([])).toBe(-1);
  });
});

describe('generateProductCode', () => {
  it('should generate code with name, weight and color', () => {
    const code = generateProductCode('纱线', '280克', '01');
    expect(code.length).toBeGreaterThan(0);
    expect(code).toMatch(/^[A-Z0-9]+$/);
  });

  it('should generate different codes for different inputs', () => {
    const code1 = generateProductCode('纱线A', '280', '01');
    const code2 = generateProductCode('纱线B', '320', '02');
    expect(code1).not.toBe(code2);
  });

  it('should handle empty inputs', () => {
    const code = generateProductCode('', '', '');
    expect(code).toBe('XX000');
  });
});

describe('debounce', () => {
  beforeEach(() => {
    jest.useFakeTimers();
  });

  afterEach(() => {
    jest.useRealTimers();
  });

  it('should delay function call', () => {
    const fn = jest.fn();
    const debouncedFn = debounce(fn, 100);

    debouncedFn();
    expect(fn).not.toHaveBeenCalled();

    jest.advanceTimersByTime(100);
    expect(fn).toHaveBeenCalledTimes(1);
  });

  it('should reset timer on multiple calls', () => {
    const fn = jest.fn();
    const debouncedFn = debounce(fn, 100);

    debouncedFn();
    jest.advanceTimersByTime(50);
    debouncedFn();
    jest.advanceTimersByTime(50);
    expect(fn).not.toHaveBeenCalled();

    jest.advanceTimersByTime(100);
    expect(fn).toHaveBeenCalledTimes(1);
  });

  it('should pass arguments', () => {
    const fn = jest.fn();
    const debouncedFn = debounce(fn, 100);

    debouncedFn('arg1', 'arg2');
    jest.advanceTimersByTime(100);
    expect(fn).toHaveBeenCalledWith('arg1', 'arg2');
  });
});

describe('throttle', () => {
  beforeEach(() => {
    jest.useFakeTimers();
  });

  afterEach(() => {
    jest.useRealTimers();
  });

  it('should execute immediately on first call', () => {
    const fn = jest.fn();
    const throttledFn = throttle(fn, 100);

    throttledFn();
    expect(fn).toHaveBeenCalledTimes(1);
  });

  it('should pass arguments on first call', () => {
    const fn = jest.fn();
    const throttledFn = throttle(fn, 100);

    throttledFn('arg1', 'arg2');
    expect(fn).toHaveBeenCalledWith('arg1', 'arg2');
  });
});

describe('safeParseNumber', () => {
  it('should parse valid numbers', () => {
    expect(safeParseNumber('123')).toBe(123);
    expect(safeParseNumber('123.45')).toBe(123.45);
    expect(safeParseNumber(123)).toBe(123);
  });

  it('should return default for invalid values', () => {
    expect(safeParseNumber('abc')).toBe(0);
    expect(safeParseNumber('')).toBe(0);
    expect(safeParseNumber(null)).toBe(0);
    expect(safeParseNumber(undefined)).toBe(0);
  });

  it('should use custom default', () => {
    expect(safeParseNumber('abc', 10)).toBe(10);
    expect(safeParseNumber('', -1)).toBe(-1);
  });
});

describe('safeParseInt', () => {
  it('should parse valid integers', () => {
    expect(safeParseInt('123')).toBe(123);
    expect(safeParseInt('123.99')).toBe(123);
    expect(safeParseInt(123)).toBe(123);
  });

  it('should return default for invalid values', () => {
    expect(safeParseInt('abc')).toBe(0);
    expect(safeParseInt('')).toBe(0);
  });

  it('should use custom default', () => {
    expect(safeParseInt('abc', 5)).toBe(5);
  });
});

describe('groupProductsByNameAndWeight', () => {
  it('should group products correctly', () => {
    const products = [
      { id: '1', product_name: 'Product A', weight: 100 },
      { id: '2', product_name: 'Product A', weight: 100 },
      { id: '3', product_name: 'Product B', weight: 200 },
    ];
    const grouped = groupProductsByNameAndWeight(products);
    expect(Object.keys(grouped).length).toBeGreaterThanOrEqual(1);
  });

  it('should handle empty array', () => {
    const result = groupProductsByNameAndWeight([]);
    expect(Array.isArray(result)).toBe(true);
  });
});

describe('calculateSummary', () => {
  it('should calculate summary correctly', () => {
    const items = [
      { raw_fabric_rolls: 10, raw_fabric_meters: 100 },
      { raw_fabric_rolls: 20, raw_fabric_meters: 200 },
      { raw_fabric_rolls: 30, raw_fabric_meters: 300 },
    ];
    const summary = calculateSummary(items);
    expect(summary.totalRolls).toBeGreaterThan(0);
    expect(summary.totalMeters).toBeGreaterThan(0);
  });

  it('should handle empty array', () => {
    const summary = calculateSummary([]);
    expect(summary).toBeDefined();
  });
});
