import { describe, expect, it } from 'vitest';
import {
  REPORTING_PAGE_SIZE_MAX,
  comparisonRange,
  parseReportingFilters,
} from '../application/filters.js';

const FROM = '2026-08-01T00:00:00Z';
const TO = '2026-08-31T23:59:59Z';
const UUID_A = '11111111-1111-4111-8111-111111111001';
const UUID_B = '22222222-2222-4222-8222-222222222002';

describe('reportingFiltersSchema / parseReportingFilters', () => {
  it('parses a valid query and applies defaults (AC1)', () => {
    const f = parseReportingFilters({ from: FROM, to: TO });

    expect(f.range).toEqual({ from: FROM, to: TO });
    expect(f.compare).toBe('none');
    expect(f.channel).toBe('all');
    expect(f.storeIds).toEqual([]);
    expect(f.page).toBe(1);
    expect(f.pageSize).toBe(50);
  });

  it('accepts repeated and single storeId as a UUID array', () => {
    expect(parseReportingFilters({ from: FROM, to: TO, storeId: [UUID_A, UUID_B] }).storeIds).toEqual([
      UUID_A,
      UUID_B,
    ]);
    expect(parseReportingFilters({ from: FROM, to: TO, storeId: UUID_A }).storeIds).toEqual([
      UUID_A,
    ]);
  });

  it('rejects inverted range (from >= to) with a validation error (AC1)', () => {
    const same = { from: FROM, to: FROM };
    expect(() => parseReportingFilters(same)).toThrow();
    expect(() => parseReportingFilters({ from: TO, to: FROM })).toThrow();
  });

  it('rejects invalid UUIDs in repeatable fields (AC4)', () => {
    expect(() => parseReportingFilters({ from: FROM, to: TO, storeId: 'not-a-uuid' })).toThrow();
  });

  it('rejects non-ISO datetimes and out-of-range page sizes', () => {
    expect(() => parseReportingFilters({ from: 'nope', to: TO })).toThrow();
    expect(() => parseReportingFilters({ from: FROM, to: TO, pageSize: 9999 })).toThrow();
  });

  it('enforces pageSize ceiling via REPORTING_PAGE_SIZE_MAX', () => {
    expect(REPORTING_PAGE_SIZE_MAX).toBe(200);
    expect(() => parseReportingFilters({ from: FROM, to: TO, pageSize: 201 })).toThrow();
  });
});

describe('comparisonRange', () => {
  it('returns null for "none"', () => {
    expect(comparisonRange({ from: FROM, to: TO }, 'none')).toBeNull();
  });

  it('"previous_equal" mirrors the exact duration, ending at the range start (AC3)', () => {
    const prev = comparisonRange({ from: FROM, to: TO }, 'previous_equal');
    expect(prev).not.toBeNull();
    const start = new Date(FROM).getTime();
    const end = new Date(TO).getTime();
    const duration = end - start;
    expect(prev!.to).toBe(new Date(start).toISOString());
    expect(prev!.from).toBe(new Date(start - duration).toISOString());
    expect(new Date(prev!.from).getTime()).toBeLessThan(start);
    expect(new Date(prev!.to).getTime()).toBe(start);
  });

  it('"previous_calendar" is UTC-aligned and ends at the range start (AC4)', () => {
    const prev = comparisonRange({ from: FROM, to: TO }, 'previous_calendar');
    expect(prev).not.toBeNull();
    const thisStart = new Date(FROM).getTime();
    expect(prev!.to).toBe(new Date(thisStart).toISOString());
    expect(new Date(prev!.from).getTime()).toBeLessThan(thisStart);
    expect(new Date(prev!.to).getTime()).toBe(thisStart);
  });
});
