import { describe, it, expect } from 'vitest';
import {
  NAME_MAP,
  parseCSV,
  parseFloatMaybe,
  parseIntMaybe,
  getVariationNote,
  resolveExerciseName,
  parseDate,
} from '@/lib/csvParser';

describe('parseCSV', () => {
  it('returns [] for empty input', () => {
    expect(parseCSV('')).toEqual([]);
    expect(parseCSV('   \n  \n')).toEqual([]);
  });

  it('parses a simple header + rows', () => {
    const out = parseCSV('a,b,c\n1,2,3\n4,5,6\n');
    expect(out).toEqual([
      { a: '1', b: '2', c: '3' },
      { a: '4', b: '5', c: '6' },
    ]);
  });

  it('lowercases header keys (case-insensitive lookup)', () => {
    const out = parseCSV('Date,Exercise\n2026-01-01,Bench\n');
    expect(out[0]).toEqual({ date: '2026-01-01', exercise: 'Bench' });
  });

  it('handles quoted fields with commas', () => {
    const out = parseCSV('a,b\n"hello, world","x"\n');
    expect(out[0]).toEqual({ a: 'hello, world', b: 'x' });
  });

  it('handles escaped double quotes inside quoted fields', () => {
    const out = parseCSV('a\n"she said ""hi"""\n');
    expect(out[0].a).toBe('she said "hi"');
  });

  it('drops cells with empty/whitespace values', () => {
    const out = parseCSV('a,b,c\n1,,3\n');
    expect(out[0]).toEqual({ a: '1', c: '3' });
  });

  it('normalizes CRLF line endings', () => {
    const out = parseCSV('a,b\r\n1,2\r\n');
    expect(out).toEqual([{ a: '1', b: '2' }]);
  });

  it('ignores fully-empty lines', () => {
    const out = parseCSV('a,b\n\n1,2\n\n3,4\n');
    expect(out).toEqual([
      { a: '1', b: '2' },
      { a: '3', b: '4' },
    ]);
  });
});

describe('parseFloatMaybe / parseIntMaybe', () => {
  it('returns undefined for empty / undefined / non-numeric', () => {
    expect(parseFloatMaybe(undefined)).toBeUndefined();
    expect(parseFloatMaybe('')).toBeUndefined();
    expect(parseFloatMaybe('not a number')).toBeUndefined();
    expect(parseIntMaybe(undefined)).toBeUndefined();
  });

  it('parses valid numbers', () => {
    expect(parseFloatMaybe('42.5')).toBe(42.5);
    expect(parseIntMaybe('17')).toBe(17);
    expect(parseIntMaybe('17.9')).toBe(17); // int truncates
  });
});

describe('getVariationNote', () => {
  it('detects each known variation', () => {
    expect(getVariationNote('Chinup (Narrow)')).toBe('narrow');
    expect(getVariationNote('Chinup Negatives')).toBe('negatives');
    expect(getVariationNote('Squat (Foot Elevated)')).toBe('foot elevated');
  });

  it('returns null for unknown variations', () => {
    expect(getVariationNote('Bench Press')).toBeNull();
    expect(getVariationNote('Squat')).toBeNull();
  });
});

describe('resolveExerciseName', () => {
  it('maps every NAME_MAP entry to its canonical target', () => {
    for (const [shorthand, canonical] of Object.entries(NAME_MAP)) {
      expect(resolveExerciseName(shorthand)).toBe(canonical);
    }
  });

  it('passes through unknown names unchanged', () => {
    expect(resolveExerciseName('Bench Press')).toBe('Bench Press');
    expect(resolveExerciseName('Some Brand New Exercise')).toBe(
      'Some Brand New Exercise',
    );
  });

  it('handles the canonical examples from the route header comment', () => {
    expect(resolveExerciseName('BB Row')).toBe('Barbell Row');
    expect(resolveExerciseName('CoC')).toBe('Captains of Crush');
    expect(resolveExerciseName('Hex DL')).toBe('Hex Bar Deadlift');
  });
});

describe('parseDate', () => {
  it('parses M/D/YYYY (US-style) into noon-UTC ISO', () => {
    expect(parseDate('1/27/2026')).toBe('2026-01-27T12:00:00.000Z');
    // Single-digit month + day pad correctly.
    expect(parseDate('3/5/2026')).toBe('2026-03-05T12:00:00.000Z');
  });

  it('parses YYYY-MM-DD (ISO) into noon-UTC', () => {
    expect(parseDate('2026-04-21')).toBe('2026-04-21T12:00:00.000Z');
  });

  it('produces dates that fall on the same calendar day in US timezones', () => {
    // Anchor at noon UTC means even US Pacific (UTC-8) sees the same day.
    const out = parseDate('1/27/2026');
    const d = new Date(out);
    // 12:00 UTC = 04:00 PST = 05:00 PDT — still Jan 27 in Pacific.
    expect(d.getUTCDate()).toBe(27);
  });
});
