import { describe, it, expect } from 'vitest';
import {
  formatCustom,
  formatDate,
  formatIso,
  formatSmart,
  type DateFormatMode,
} from './date-format';

/** A fixed local reference "now" for relative-date tests. */
const NOW = new Date(2026, 5, 7, 12, 0, 0); // 2026-06-07 12:00 local
const at = (y: number, mo: number, d: number, h = 9, mi = 0, s = 0) => new Date(y, mo, d, h, mi, s);

describe('formatSmart', () => {
  it('renders the same calendar day as Today', () => {
    expect(formatSmart(at(2026, 5, 7, 0, 1), NOW)).toBe('Today');
    expect(formatSmart(at(2026, 5, 7, 23, 59), NOW)).toBe('Today');
  });

  it('renders the previous calendar day as Yesterday', () => {
    expect(formatSmart(at(2026, 5, 6), NOW)).toBe('Yesterday');
  });

  it('renders under a week as Nd ago', () => {
    expect(formatSmart(at(2026, 5, 4), NOW)).toBe('3d ago');
    expect(formatSmart(at(2026, 5, 1), NOW)).toBe('6d ago');
  });

  it('falls back to a full locale date at a week or older', () => {
    const old = at(2026, 4, 1);
    expect(formatSmart(old, NOW)).toBe(old.toLocaleDateString());
  });

  it('does not flip to Yesterday on the hour within the same day', () => {
    // 1 minute ago but same calendar day → still Today.
    expect(formatSmart(at(2026, 5, 7, 11, 59), NOW)).toBe('Today');
  });
});

describe('formatIso', () => {
  it('returns a full ISO 8601 timestamp', () => {
    const d = new Date('2026-06-07T10:30:00.000Z');
    expect(formatIso(d)).toBe('2026-06-07T10:30:00.000Z');
  });
});

describe('formatCustom', () => {
  it('formats the placeholder pattern yyyy-MM-dd HH:mm', () => {
    expect(formatCustom(at(2026, 0, 5, 8, 4), 'yyyy-MM-dd HH:mm')).toBe('2026-01-05 08:04');
  });

  it('supports month names and short year', () => {
    expect(formatCustom(at(2026, 11, 25), 'd MMMM yy')).toBe('25 December 26');
    expect(formatCustom(at(2026, 2, 9), 'MMM d, yyyy')).toBe('Mar 9, 2026');
  });

  it('supports 12-hour clock with meridiem', () => {
    expect(formatCustom(at(2026, 5, 7, 0, 5), 'h:mm a')).toBe('12:05 AM');
    expect(formatCustom(at(2026, 5, 7, 13, 5), 'h:mm a')).toBe('1:05 PM');
    expect(formatCustom(at(2026, 5, 7, 12, 0), 'hh:mm a')).toBe('12:00 PM');
  });

  it('preserves non-token literals verbatim', () => {
    expect(formatCustom(at(2026, 0, 1), 'yyyy/MM/dd')).toBe('2026/01/01');
  });
});

describe('formatDate', () => {
  it('dispatches by mode', () => {
    const iso = at(2026, 5, 6, 9, 0).toISOString();
    expect(formatDate(iso, 'smart', '', NOW)).toBe('Yesterday');
    expect(formatDate(iso, 'iso')).toBe(new Date(iso).toISOString());
    expect(formatDate(iso, 'custom', 'yyyy-MM-dd', NOW)).toBe('2026-06-06');
  });

  it('falls back to ISO when a custom pattern is blank', () => {
    const iso = '2026-06-07T10:30:00.000Z';
    expect(formatDate(iso, 'custom', '   ')).toBe(new Date(iso).toISOString());
    expect(formatDate(iso, 'custom', '')).toBe(new Date(iso).toISOString());
  });

  it('returns an unparseable timestamp verbatim rather than "Invalid Date"', () => {
    expect(formatDate('not-a-date', 'iso')).toBe('not-a-date');
    expect(formatDate('not-a-date', 'smart', '', NOW)).toBe('not-a-date');
  });

  it('defaults unknown modes to smart', () => {
    const iso = at(2026, 5, 7).toISOString();
    expect(formatDate(iso, 'bogus' as DateFormatMode, '', NOW)).toBe('Today');
  });
});
