/**
 * Unit tests for feature flags module — types + validation.
 */

import { describe, it, expect } from 'vitest';
import { CreateFlagSchema, UpdateFlagSchema } from './types.js';

describe('CreateFlagSchema', () => {
  it('accepts valid flag with defaults', () => {
    const result = CreateFlagSchema.safeParse({ key: 'dark_mode' });
    expect(result.success).toBe(true);
    if (result.success) {
      expect(result.data.enabled).toBe(true);
      expect(result.data.percentage).toBe(100);
      expect(result.data.platforms).toEqual([]);
      expect(result.data.regions).toEqual([]);
      expect(result.data.osVersions).toEqual([]);
      expect(result.data.segments).toEqual([]);
    }
  });

  it('accepts full input', () => {
    const result = CreateFlagSchema.safeParse({
      key: 'new_ui',
      enabled: false,
      description: 'New dashboard UI',
      platforms: ['web', 'ios'],
      regions: ['us', 'eu'],
      osVersions: [
        { platform: 'ios', minVersion: '17.0', maxVersion: '18.0' },
        { platform: 'android', minVersion: '14.0' },
      ],
      segments: ['beta'],
      percentage: 50,
    });
    expect(result.success).toBe(true);
    if (result.success) {
      expect(result.data.regions).toEqual(['us', 'eu']);
      expect(result.data.osVersions).toHaveLength(2);
    }
  });

  it('accepts osVersions with only minVersion', () => {
    const result = CreateFlagSchema.safeParse({
      key: 'ios_only',
      osVersions: [{ platform: 'ios', minVersion: '16.0' }],
    });
    expect(result.success).toBe(true);
  });

  it('accepts osVersions with only maxVersion', () => {
    const result = CreateFlagSchema.safeParse({
      key: 'legacy_only',
      osVersions: [{ platform: 'android', maxVersion: '13.0' }],
    });
    expect(result.success).toBe(true);
  });

  it('rejects osVersions with empty platform', () => {
    const result = CreateFlagSchema.safeParse({
      key: 'bad_os',
      osVersions: [{ platform: '' }],
    });
    expect(result.success).toBe(false);
  });

  it('rejects key with spaces', () => {
    const result = CreateFlagSchema.safeParse({ key: 'dark mode' });
    expect(result.success).toBe(false);
  });

  it('rejects key with uppercase', () => {
    const result = CreateFlagSchema.safeParse({ key: 'DarkMode' });
    expect(result.success).toBe(false);
  });

  it('rejects percentage > 100', () => {
    const result = CreateFlagSchema.safeParse({ key: 'test', percentage: 150 });
    expect(result.success).toBe(false);
  });

  it('rejects percentage < 0', () => {
    const result = CreateFlagSchema.safeParse({ key: 'test', percentage: -10 });
    expect(result.success).toBe(false);
  });
});

describe('UpdateFlagSchema', () => {
  it('accepts partial updates', () => {
    const result = UpdateFlagSchema.safeParse({
      enabled: false,
      percentage: 25,
    });
    expect(result.success).toBe(true);
  });

  it('accepts empty object', () => {
    const result = UpdateFlagSchema.safeParse({});
    expect(result.success).toBe(true);
  });

  it('accepts platform list update', () => {
    const result = UpdateFlagSchema.safeParse({
      platforms: ['ios', 'android'],
    });
    expect(result.success).toBe(true);
  });

  it('accepts regions update', () => {
    const result = UpdateFlagSchema.safeParse({
      regions: ['us', 'apac'],
    });
    expect(result.success).toBe(true);
  });

  it('accepts osVersions update', () => {
    const result = UpdateFlagSchema.safeParse({
      osVersions: [{ platform: 'ios', minVersion: '17.0', maxVersion: '18.0' }],
    });
    expect(result.success).toBe(true);
  });
});

describe('compareVersions', () => {
  let compareVersions: (a: string, b: string) => number;

  beforeAll(async () => {
    const mod = await import('./routes.js');
    compareVersions = mod.compareVersions;
  });

  it('equal versions return 0', () => {
    expect(compareVersions('17.2.1', '17.2.1')).toBe(0);
  });

  it('a < b returns -1', () => {
    expect(compareVersions('16.4', '17.0')).toBe(-1);
    expect(compareVersions('17.0.0', '17.0.1')).toBe(-1);
  });

  it('a > b returns 1', () => {
    expect(compareVersions('18.0', '17.2.1')).toBe(1);
    expect(compareVersions('17.1', '17.0.9')).toBe(1);
  });

  it('handles different segment lengths', () => {
    expect(compareVersions('17.0', '17.0.0')).toBe(0);
    expect(compareVersions('17', '17.0.0')).toBe(0);
    expect(compareVersions('17.0.1', '17')).toBe(1);
  });

  it('handles single-segment versions', () => {
    expect(compareVersions('14', '15')).toBe(-1);
    expect(compareVersions('15', '14')).toBe(1);
  });
});

describe('hashUserFlag (deterministic A/B assignment)', () => {
  // Import the hash function from routes
  let hashUserFlag: (userId: string, flagKey: string) => number;

  beforeAll(async () => {
    const mod = await import('./routes.js');
    hashUserFlag = mod.hashUserFlag;
  });

  it('returns the same bucket for the same user+flag', () => {
    const a = hashUserFlag('user_123', 'dark_mode');
    const b = hashUserFlag('user_123', 'dark_mode');
    const c = hashUserFlag('user_123', 'dark_mode');
    expect(a).toBe(b);
    expect(b).toBe(c);
  });

  it('returns different buckets for different users', () => {
    const a = hashUserFlag('user_1', 'feature_x');
    const b = hashUserFlag('user_2', 'feature_x');
    // Statistically they should differ (not guaranteed, but extremely likely)
    // We test multiple pairs to be safe
    expect(a).not.toBe(b);
    const buckets = new Set<number>();
    for (let i = 0; i < 20; i++) {
      buckets.add(hashUserFlag(`user_${i}`, 'feature_x'));
    }
    // At least 5 distinct buckets out of 20 users
    expect(buckets.size).toBeGreaterThanOrEqual(5);
  });

  it('returns different buckets for different flags', () => {
    const a = hashUserFlag('user_42', 'flag_a');
    const b = hashUserFlag('user_42', 'flag_b');
    expect(a).not.toBe(b);
  });

  it('always returns 0-99', () => {
    for (let i = 0; i < 100; i++) {
      const bucket = hashUserFlag(`user_${i}`, `flag_${i}`);
      expect(bucket).toBeGreaterThanOrEqual(0);
      expect(bucket).toBeLessThan(100);
    }
  });

  it('produces roughly uniform distribution', () => {
    // Hash 1000 users, check that no bucket range gets >70% or <30%
    let below50 = 0;
    const total = 1000;
    for (let i = 0; i < total; i++) {
      if (hashUserFlag(`user_${i}`, 'ab_test') < 50) below50++;
    }
    const ratio = below50 / total;
    expect(ratio).toBeGreaterThan(0.3);
    expect(ratio).toBeLessThan(0.7);
  });
});
