import { describe, it, expect } from 'vitest';
import { parseResponsive, BP_NAMES, BREAKPOINTS } from './responsive.js';

// ── parseResponsive ────────────────────────────────────────────────────────

describe('parseResponsive — scalar fast path', () => {
  it('returns value unchanged when no @ present', () => {
    expect(parseResponsive('3',        'md')).toBe('3');
    expect(parseResponsive('auto-fill','lg')).toBe('auto-fill');
    expect(parseResponsive('md',       'sm')).toBe('md');
  });

  it('returns null for null / undefined input', () => {
    expect(parseResponsive(null,      'md')).toBeNull();
    expect(parseResponsive(undefined, 'md')).toBeNull();
  });

  it('returns null for empty string', () => {
    expect(parseResponsive('', 'md')).toBeNull();
  });
});

describe('parseResponsive — mobile-first resolution', () => {
  // "1 2@sm 4@lg" = 1 at xs, 2 from sm, 4 from lg
  const val = '1 2@sm 4@lg';

  it('base (xs) — no annotation matches, returns unannotated default', () => {
    expect(parseResponsive(val, 'xs')).toBe('1');
  });

  it('sm — @sm matches exactly', () => {
    expect(parseResponsive(val, 'sm')).toBe('2');
  });

  it('md — largest annotation ≤ md is @sm', () => {
    expect(parseResponsive(val, 'md')).toBe('2');
  });

  it('lg — @lg matches exactly', () => {
    expect(parseResponsive(val, 'lg')).toBe('4');
  });

  it('xl — largest annotation ≤ xl is @lg', () => {
    expect(parseResponsive(val, 'xl')).toBe('4');
  });
});

describe('parseResponsive — sparse annotations (two-point)', () => {
  it('"4 1@sm": 4 at xs, 1 from sm upward', () => {
    expect(parseResponsive('4 1@sm', 'xs')).toBe('4');
    expect(parseResponsive('4 1@sm', 'sm')).toBe('1');
    expect(parseResponsive('4 1@sm', 'md')).toBe('1');
    expect(parseResponsive('4 1@sm', 'lg')).toBe('1');
  });

  it('"2@md": no default — returns null below md, 2 from md up', () => {
    expect(parseResponsive('2@md', 'xs')).toBeNull();
    expect(parseResponsive('2@md', 'sm')).toBeNull();
    expect(parseResponsive('2@md', 'md')).toBe('2');
    expect(parseResponsive('2@md', 'xl')).toBe('2');
  });

  it('"1 4@lg": only one override — md falls back to base', () => {
    expect(parseResponsive('1 4@lg', 'xs')).toBe('1');
    expect(parseResponsive('1 4@lg', 'md')).toBe('1');
    expect(parseResponsive('1 4@lg', 'lg')).toBe('4');
    expect(parseResponsive('1 4@lg', 'xl')).toBe('4');
  });
});

describe('parseResponsive — order-independence', () => {
  it('annotations in any order resolve identically', () => {
    const a = '1 2@sm 4@lg';
    const b = '4@lg 1 2@sm';
    const c = '2@sm 4@lg 1';
    for (const bp of BP_NAMES) {
      const ref = parseResponsive(a, bp);
      expect(parseResponsive(b, bp)).toBe(ref);
      expect(parseResponsive(c, bp)).toBe(ref);
    }
  });
});

describe('parseResponsive — edge cases', () => {
  it('unknown bp annotation is silently skipped; default still returned', () => {
    expect(parseResponsive('3 1@xxl', 'xl')).toBe('3');
  });

  it('single annotation with no default', () => {
    expect(parseResponsive('2@sm', 'xs')).toBeNull();
    expect(parseResponsive('2@sm', 'sm')).toBe('2');
  });

  it('all five breakpoints annotated — each resolves to its own value', () => {
    const val = '0 1@xs 2@sm 3@md 4@lg 5@xl';
    expect(parseResponsive(val, 'xs')).toBe('1');
    expect(parseResponsive(val, 'sm')).toBe('2');
    expect(parseResponsive(val, 'md')).toBe('3');
    expect(parseResponsive(val, 'lg')).toBe('4');
    expect(parseResponsive(val, 'xl')).toBe('5');
  });
});

// ── BREAKPOINTS contract ───────────────────────────────────────────────────

describe('BREAKPOINTS / BP_NAMES exports', () => {
  it('BP_NAMES is ordered narrowest → widest', () => {
    expect(BP_NAMES).toEqual(['xs', 'sm', 'md', 'lg', 'xl']);
  });

  it('BREAKPOINTS values are strictly increasing', () => {
    const vals = BP_NAMES.map((bp) => BREAKPOINTS[bp]);
    for (let i = 1; i < vals.length; i++) {
      expect(vals[i]).toBeGreaterThan(vals[i - 1]);
    }
  });

  it('xs starts at 0', () => {
    expect(BREAKPOINTS.xs).toBe(0);
  });
});
