/**
 * Unit tests for waitlist module — types, schemas, helpers.
 */

import { describe, it, expect } from 'vitest';
import {
  JoinWaitlistSchema,
  CheckStatusSchema,
  UnsubscribeSchema,
  UpdateWaitlistEntrySchema,
  WaitlistQuerySchema,
  BatchInviteSchema,
} from './types.js';
import { normalizeEmail } from './repository.js';
import { isValidStatusTransition } from '../products/types.js';

// ── Email normalization ──

describe('normalizeEmail', () => {
  it('lowercases email', () => {
    expect(normalizeEmail('User@Gmail.COM')).toBe('user@gmail.com');
  });

  it('trims whitespace', () => {
    expect(normalizeEmail('  user@test.com  ')).toBe('user@test.com');
  });

  it('handles already normalized email', () => {
    expect(normalizeEmail('user@test.com')).toBe('user@test.com');
  });
});

// ── JoinWaitlistSchema ──

describe('JoinWaitlistSchema', () => {
  it('accepts valid minimal input', () => {
    const result = JoinWaitlistSchema.safeParse({
      email: 'user@example.com',
    });
    expect(result.success).toBe(true);
    if (result.success) {
      expect(result.data.email).toBe('user@example.com');
      expect(result.data.source).toBe('organic');
      expect(result.data.customData).toEqual({});
    }
  });

  it('accepts full input with all fields', () => {
    const result = JoinWaitlistSchema.safeParse({
      email: 'user@example.com',
      name: 'John Doe',
      source: 'referral',
      ref: 'wl_abc123',
      customData: { use_case: 'dictation', company_size: '10-50' },
      utmSource: 'twitter',
      utmMedium: 'social',
      utmCampaign: 'launch2026',
    });
    expect(result.success).toBe(true);
    if (result.success) {
      expect(result.data.name).toBe('John Doe');
      expect(result.data.source).toBe('referral');
      expect(result.data.ref).toBe('wl_abc123');
      expect(result.data.customData).toEqual({ use_case: 'dictation', company_size: '10-50' });
    }
  });

  it('rejects invalid email', () => {
    const result = JoinWaitlistSchema.safeParse({
      email: 'not-an-email',
    });
    expect(result.success).toBe(false);
  });

  it('rejects empty email', () => {
    const result = JoinWaitlistSchema.safeParse({
      email: '',
    });
    expect(result.success).toBe(false);
  });

  it('rejects missing email', () => {
    const result = JoinWaitlistSchema.safeParse({});
    expect(result.success).toBe(false);
  });

  it('rejects invalid source', () => {
    const result = JoinWaitlistSchema.safeParse({
      email: 'user@example.com',
      source: 'unknown_source',
    });
    expect(result.success).toBe(false);
  });

  it('accepts all valid source values', () => {
    for (const source of ['organic', 'referral', 'social', 'ad', 'api']) {
      const result = JoinWaitlistSchema.safeParse({
        email: 'user@example.com',
        source,
      });
      expect(result.success).toBe(true);
    }
  });
});

// ── CheckStatusSchema ──

describe('CheckStatusSchema', () => {
  it('accepts valid email + token', () => {
    const result = CheckStatusSchema.safeParse({
      email: 'user@example.com',
      unsubscribeToken: 'abc-123-token',
    });
    expect(result.success).toBe(true);
  });

  it('rejects missing token', () => {
    const result = CheckStatusSchema.safeParse({
      email: 'user@example.com',
    });
    expect(result.success).toBe(false);
  });

  it('rejects empty token', () => {
    const result = CheckStatusSchema.safeParse({
      email: 'user@example.com',
      unsubscribeToken: '',
    });
    expect(result.success).toBe(false);
  });

  it('rejects invalid email', () => {
    const result = CheckStatusSchema.safeParse({
      email: 'bad',
      unsubscribeToken: 'token',
    });
    expect(result.success).toBe(false);
  });
});

// ── UnsubscribeSchema ──

describe('UnsubscribeSchema', () => {
  it('accepts valid input', () => {
    const result = UnsubscribeSchema.safeParse({
      email: 'user@example.com',
      unsubscribeToken: 'token-123',
    });
    expect(result.success).toBe(true);
  });

  it('rejects missing fields', () => {
    expect(UnsubscribeSchema.safeParse({}).success).toBe(false);
    expect(UnsubscribeSchema.safeParse({ email: 'a@b.com' }).success).toBe(false);
    expect(UnsubscribeSchema.safeParse({ unsubscribeToken: 'x' }).success).toBe(false);
  });
});

// ── UpdateWaitlistEntrySchema (admin) ──

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

  it('accepts status change', () => {
    const result = UpdateWaitlistEntrySchema.safeParse({ status: 'invited' });
    expect(result.success).toBe(true);
  });

  it('accepts priority change', () => {
    const result = UpdateWaitlistEntrySchema.safeParse({ priority: 5 });
    expect(result.success).toBe(true);
  });

  it('rejects invalid status', () => {
    const result = UpdateWaitlistEntrySchema.safeParse({ status: 'deleted' });
    expect(result.success).toBe(false);
  });

  it('rejects negative priority', () => {
    const result = UpdateWaitlistEntrySchema.safeParse({ priority: -1 });
    expect(result.success).toBe(false);
  });

  it('accepts all valid statuses', () => {
    for (const status of ['pending', 'invited', 'converted', 'unsubscribed']) {
      expect(UpdateWaitlistEntrySchema.safeParse({ status }).success).toBe(true);
    }
  });
});

// ── WaitlistQuerySchema ──

describe('WaitlistQuerySchema', () => {
  it('accepts empty query (defaults)', () => {
    const result = WaitlistQuerySchema.safeParse({});
    expect(result.success).toBe(true);
    if (result.success) {
      expect(result.data.sortBy).toBe('position');
      expect(result.data.sortOrder).toBe('asc');
      expect(result.data.limit).toBe(50);
      expect(result.data.offset).toBe(0);
    }
  });

  it('accepts full query with filters', () => {
    const result = WaitlistQuerySchema.safeParse({
      productId: 'lysnrai',
      status: 'pending',
      source: 'referral',
      q: 'john',
      sortBy: 'priority',
      sortOrder: 'desc',
      limit: '100',
      offset: '50',
    });
    expect(result.success).toBe(true);
    if (result.success) {
      expect(result.data.productId).toBe('lysnrai');
      expect(result.data.status).toBe('pending');
      expect(result.data.limit).toBe(100);
    }
  });

  it('coerces string numbers to numbers', () => {
    const result = WaitlistQuerySchema.safeParse({
      limit: '25',
      offset: '10',
    });
    expect(result.success).toBe(true);
    if (result.success) {
      expect(result.data.limit).toBe(25);
      expect(result.data.offset).toBe(10);
    }
  });

  it('rejects limit > 500', () => {
    const result = WaitlistQuerySchema.safeParse({ limit: '501' });
    expect(result.success).toBe(false);
  });
});

// ── BatchInviteSchema ──

describe('BatchInviteSchema', () => {
  it('accepts valid input', () => {
    const result = BatchInviteSchema.safeParse({ count: 50 });
    expect(result.success).toBe(true);
    if (result.success) {
      expect(result.data.strategy).toBe('fifo');
    }
  });

  it('accepts all strategies', () => {
    for (const strategy of ['fifo', 'priority', 'random']) {
      const result = BatchInviteSchema.safeParse({ count: 10, strategy });
      expect(result.success).toBe(true);
    }
  });

  it('rejects count < 1', () => {
    expect(BatchInviteSchema.safeParse({ count: 0 }).success).toBe(false);
  });

  it('rejects count > 500', () => {
    expect(BatchInviteSchema.safeParse({ count: 501 }).success).toBe(false);
  });

  it('rejects missing count', () => {
    expect(BatchInviteSchema.safeParse({}).success).toBe(false);
  });

  it('rejects invalid strategy', () => {
    expect(BatchInviteSchema.safeParse({ count: 10, strategy: 'invalid' }).success).toBe(false);
  });
});

// ── Product status transitions ──

describe('isValidStatusTransition', () => {
  it('allows draft → pre_launch', () => {
    expect(isValidStatusTransition('draft', 'pre_launch')).toBe(true);
  });

  it('allows draft → disabled', () => {
    expect(isValidStatusTransition('draft', 'disabled')).toBe(true);
  });

  it('allows pre_launch → beta', () => {
    expect(isValidStatusTransition('pre_launch', 'beta')).toBe(true);
  });

  it('allows pre_launch → active (skip beta)', () => {
    expect(isValidStatusTransition('pre_launch', 'active')).toBe(true);
  });

  it('allows beta → active', () => {
    expect(isValidStatusTransition('beta', 'active')).toBe(true);
  });

  it('allows active → sunset', () => {
    expect(isValidStatusTransition('active', 'sunset')).toBe(true);
  });

  it('allows sunset → disabled', () => {
    expect(isValidStatusTransition('sunset', 'disabled')).toBe(true);
  });

  it('allows disabled → draft (re-enable cycle)', () => {
    expect(isValidStatusTransition('disabled', 'draft')).toBe(true);
  });

  it('rejects draft → active (skip pre_launch)', () => {
    expect(isValidStatusTransition('draft', 'active')).toBe(false);
  });

  it('rejects active → draft (backward)', () => {
    expect(isValidStatusTransition('active', 'draft')).toBe(false);
  });

  it('rejects active → pre_launch (backward)', () => {
    expect(isValidStatusTransition('active', 'pre_launch')).toBe(false);
  });

  it('allows no-op (same status)', () => {
    expect(isValidStatusTransition('active', 'active')).toBe(true);
    expect(isValidStatusTransition('draft', 'draft')).toBe(true);
  });
});

// ── Route exports ──

describe('waitlistRoutes export', () => {
  it('exports waitlistRoutes function', async () => {
    const mod = await import('./routes.js');
    expect(typeof mod.waitlistRoutes).toBe('function');
  });
});
