/**
 * Component: Release Date Utilities Tests
 * Documentation: documentation/backend/database.md
 */

import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { isUnreleased, shouldSkipAutoSearch } from '@/lib/utils/release-date';

describe('isUnreleased', () => {
  it('returns false for null', () => {
    expect(isUnreleased(null)).toBe(false);
  });

  it('returns false for undefined', () => {
    expect(isUnreleased(undefined)).toBe(false);
  });

  it('returns false for empty string', () => {
    expect(isUnreleased('')).toBe(false);
  });

  it('returns false for malformed string', () => {
    expect(isUnreleased('not-a-date')).toBe(false);
  });

  it('returns false when release date is today (UTC date-only)', () => {
    const now = new Date();
    const today = new Date(Date.UTC(
      now.getUTCFullYear(),
      now.getUTCMonth(),
      now.getUTCDate()
    ));
    expect(isUnreleased(today)).toBe(false);
  });

  it('returns false when release date is yesterday', () => {
    const now = new Date();
    const yesterday = new Date(Date.UTC(
      now.getUTCFullYear(),
      now.getUTCMonth(),
      now.getUTCDate() - 1
    ));
    expect(isUnreleased(yesterday)).toBe(false);
  });

  it('returns true when release date is tomorrow', () => {
    const now = new Date();
    const tomorrow = new Date(Date.UTC(
      now.getUTCFullYear(),
      now.getUTCMonth(),
      now.getUTCDate() + 1
    ));
    expect(isUnreleased(tomorrow)).toBe(true);
  });

  it('returns true for far-future ISO date string', () => {
    expect(isUnreleased('2099-01-01')).toBe(true);
  });

  it('returns true for far-future ISO datetime string', () => {
    expect(isUnreleased('2099-01-01T00:00:00Z')).toBe(true);
  });

  it('returns false for far-past Date object', () => {
    expect(isUnreleased(new Date('1990-01-01'))).toBe(false);
  });

  it('returns true for far-future Date object', () => {
    expect(isUnreleased(new Date('2099-01-01'))).toBe(true);
  });

  describe('UTC boundary cases with fake timers', () => {
    beforeEach(() => {
      vi.useFakeTimers();
    });

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

    it('treats same UTC day as released regardless of clock time', () => {
      // Pin "now" to mid-day UTC on 2026-06-15
      vi.setSystemTime(new Date('2026-06-15T12:00:00Z'));

      // A release date at the very start of the same UTC day → released
      expect(isUnreleased('2026-06-15T00:00:00Z')).toBe(false);
      // A release date at the very end of the same UTC day → released
      expect(isUnreleased('2026-06-15T23:59:59Z')).toBe(false);
    });

    it('treats next UTC day as unreleased', () => {
      vi.setSystemTime(new Date('2026-06-15T23:59:59Z'));
      expect(isUnreleased('2026-06-16T00:00:00Z')).toBe(true);
    });

    it('treats previous UTC day as released', () => {
      vi.setSystemTime(new Date('2026-06-15T00:00:00Z'));
      expect(isUnreleased('2026-06-14T23:59:59Z')).toBe(false);
    });
  });
});

describe('shouldSkipAutoSearch', () => {
  const tomorrow = new Date();
  tomorrow.setUTCDate(tomorrow.getUTCDate() + 1);

  const yesterday = new Date();
  yesterday.setUTCDate(yesterday.getUTCDate() - 1);

  it('does not skip when setting is OFF, even if unreleased', () => {
    expect(shouldSkipAutoSearch({ releaseDate: tomorrow }, false)).toEqual({
      skip: false,
    });
  });

  it('skips with reason "unreleased" when setting ON and release is in the future', () => {
    expect(shouldSkipAutoSearch({ releaseDate: tomorrow }, true)).toEqual({
      skip: true,
      reason: 'unreleased',
    });
  });

  it('does not skip when setting ON and release is in the past', () => {
    expect(shouldSkipAutoSearch({ releaseDate: yesterday }, true)).toEqual({
      skip: false,
    });
  });

  it('does not skip when setting ON and releaseDate is null', () => {
    expect(shouldSkipAutoSearch({ releaseDate: null }, true)).toEqual({
      skip: false,
    });
  });
});
