/**
 * Input Validation Tests
 */
import { describe, it, expect } from 'vitest';
import { z } from 'zod';

// Sample validation schemas (similar to what would be in validators.ts)
const UserIdSchema = z.string().min(1).max(100);
const MessageSchema = z.object({
  type: z.string().min(1).max(50),
  message: z.object({}).passthrough().optional(),
});

const CharacterCreationSchema = z.object({
  firstname: z.string().min(1).max(50),
  lastname: z.string().min(1).max(50).optional(),
  sex: z.enum(['male', 'female']),
});

const PurchaseSchema = z.object({
  itemId: z.string().min(1),
  quantity: z.number().int().positive().default(1),
});

const ConversationSchema = z.object({
  characterId: z.string().min(1),
  message: z.string().min(1).max(1000),
});

describe('Input Validation', () => {
  describe('User ID Validation', () => {
    it('should accept valid user ID', () => {
      const result = UserIdSchema.safeParse('user-123');

      expect(result.success).toBe(true);
    });

    it('should reject empty user ID', () => {
      const result = UserIdSchema.safeParse('');

      expect(result.success).toBe(false);
    });

    it('should reject too long user ID', () => {
      const result = UserIdSchema.safeParse('a'.repeat(101));

      expect(result.success).toBe(false);
    });

    it('should reject non-string user ID', () => {
      const result = UserIdSchema.safeParse(123);

      expect(result.success).toBe(false);
    });
  });

  describe('Message Validation', () => {
    it('should accept valid message', () => {
      const result = MessageSchema.safeParse({
        type: 'chat',
        message: { text: 'Hello' },
      });

      expect(result.success).toBe(true);
    });

    it('should accept message without payload', () => {
      const result = MessageSchema.safeParse({
        type: 'ping',
      });

      expect(result.success).toBe(true);
    });

    it('should reject message without type', () => {
      const result = MessageSchema.safeParse({
        message: { text: 'Hello' },
      });

      expect(result.success).toBe(false);
    });

    it('should reject empty type', () => {
      const result = MessageSchema.safeParse({
        type: '',
      });

      expect(result.success).toBe(false);
    });

    it('should reject too long type', () => {
      const result = MessageSchema.safeParse({
        type: 'a'.repeat(51),
      });

      expect(result.success).toBe(false);
    });
  });

  describe('Character Creation Validation', () => {
    it('should accept valid character data', () => {
      const result = CharacterCreationSchema.safeParse({
        firstname: 'John',
        lastname: 'Doe',
        sex: 'male',
      });

      expect(result.success).toBe(true);
    });

    it('should accept without lastname', () => {
      const result = CharacterCreationSchema.safeParse({
        firstname: 'Jane',
        sex: 'female',
      });

      expect(result.success).toBe(true);
    });

    it('should reject empty firstname', () => {
      const result = CharacterCreationSchema.safeParse({
        firstname: '',
        sex: 'male',
      });

      expect(result.success).toBe(false);
    });

    it('should reject invalid sex', () => {
      const result = CharacterCreationSchema.safeParse({
        firstname: 'Test',
        sex: 'other',
      });

      expect(result.success).toBe(false);
    });

    it('should reject too long names', () => {
      const result = CharacterCreationSchema.safeParse({
        firstname: 'a'.repeat(51),
        sex: 'male',
      });

      expect(result.success).toBe(false);
    });
  });

  describe('Purchase Validation', () => {
    it('should accept valid purchase', () => {
      const result = PurchaseSchema.safeParse({
        itemId: 'item-123',
        quantity: 5,
      });

      expect(result.success).toBe(true);
    });

    it('should default quantity to 1', () => {
      const result = PurchaseSchema.safeParse({
        itemId: 'item-123',
      });

      expect(result.success).toBe(true);
      if (result.success) {
        expect(result.data.quantity).toBe(1);
      }
    });

    it('should reject zero quantity', () => {
      const result = PurchaseSchema.safeParse({
        itemId: 'item-123',
        quantity: 0,
      });

      expect(result.success).toBe(false);
    });

    it('should reject negative quantity', () => {
      const result = PurchaseSchema.safeParse({
        itemId: 'item-123',
        quantity: -5,
      });

      expect(result.success).toBe(false);
    });

    it('should reject non-integer quantity', () => {
      const result = PurchaseSchema.safeParse({
        itemId: 'item-123',
        quantity: 2.5,
      });

      expect(result.success).toBe(false);
    });

    it('should reject empty item ID', () => {
      const result = PurchaseSchema.safeParse({
        itemId: '',
        quantity: 1,
      });

      expect(result.success).toBe(false);
    });
  });

  describe('Conversation Validation', () => {
    it('should accept valid conversation message', () => {
      const result = ConversationSchema.safeParse({
        characterId: 'char-456',
        message: 'Hello, how are you?',
      });

      expect(result.success).toBe(true);
    });

    it('should reject empty message', () => {
      const result = ConversationSchema.safeParse({
        characterId: 'char-456',
        message: '',
      });

      expect(result.success).toBe(false);
    });

    it('should reject too long message', () => {
      const result = ConversationSchema.safeParse({
        characterId: 'char-456',
        message: 'a'.repeat(1001),
      });

      expect(result.success).toBe(false);
    });

    it('should reject missing character ID', () => {
      const result = ConversationSchema.safeParse({
        message: 'Hello',
      });

      expect(result.success).toBe(false);
    });
  });

  describe('Type Coercion', () => {
    const NumberSchema = z.coerce.number();
    const BooleanSchema = z.coerce.boolean();

    it('should coerce string to number', () => {
      const result = NumberSchema.safeParse('42');

      expect(result.success).toBe(true);
      if (result.success) {
        expect(result.data).toBe(42);
      }
    });

    it('should coerce truthy values to boolean', () => {
      expect(BooleanSchema.parse('true')).toBe(true);
      expect(BooleanSchema.parse('1')).toBe(true);
      expect(BooleanSchema.parse(1)).toBe(true);
    });
  });

  describe('Sanitization', () => {
    const TrimmedStringSchema = z.string().trim();
    const LowercaseSchema = z.string().toLowerCase();

    it('should trim whitespace', () => {
      const result = TrimmedStringSchema.parse('  hello  ');

      expect(result).toBe('hello');
    });

    it('should convert to lowercase', () => {
      const result = LowercaseSchema.parse('HELLO');

      expect(result).toBe('hello');
    });
  });

  describe('Complex Nested Validation', () => {
    const GameStateSchema = z.object({
      player: z.object({
        id: z.string(),
        stats: z.object({
          health: z.number().min(0).max(100),
          energy: z.number().min(0).max(100),
        }),
      }),
      relationships: z.array(
        z.object({
          id: z.string(),
          affinity: z.number().min(-100).max(100),
        })
      ),
    });

    it('should validate nested objects', () => {
      const result = GameStateSchema.safeParse({
        player: {
          id: 'player-1',
          stats: {
            health: 80,
            energy: 60,
          },
        },
        relationships: [
          { id: 'friend-1', affinity: 50 },
          { id: 'enemy-1', affinity: -30 },
        ],
      });

      expect(result.success).toBe(true);
    });

    it('should reject invalid nested values', () => {
      const result = GameStateSchema.safeParse({
        player: {
          id: 'player-1',
          stats: {
            health: 150, // Invalid: > 100
            energy: 60,
          },
        },
        relationships: [],
      });

      expect(result.success).toBe(false);
    });
  });

  describe('Optional vs Required', () => {
    const Schema = z.object({
      required: z.string(),
      optional: z.string().optional(),
      nullable: z.string().nullish(), // nullish = optional + nullable
      defaulted: z.string().default('default-value'),
    });

    it('should require required fields', () => {
      const result = Schema.safeParse({});

      expect(result.success).toBe(false);
    });

    it('should allow missing optional fields', () => {
      const result = Schema.safeParse({
        required: 'value',
      });

      expect(result.success).toBe(true);
    });

    it('should allow null for nullable fields', () => {
      const result = Schema.safeParse({
        required: 'value',
        nullable: null,
      });

      expect(result.success).toBe(true);
    });

    it('should use default values', () => {
      const result = Schema.safeParse({
        required: 'value',
      });

      expect(result.success).toBe(true);
      if (result.success) {
        expect(result.data.defaulted).toBe('default-value');
      }
    });
  });

  describe('Union Types', () => {
    const ActionSchema = z.discriminatedUnion('type', [
      z.object({ type: z.literal('attack'), damage: z.number() }),
      z.object({ type: z.literal('heal'), amount: z.number() }),
      z.object({ type: z.literal('move'), x: z.number(), y: z.number() }),
    ]);

    it('should validate attack action', () => {
      const result = ActionSchema.safeParse({
        type: 'attack',
        damage: 50,
      });

      expect(result.success).toBe(true);
    });

    it('should validate heal action', () => {
      const result = ActionSchema.safeParse({
        type: 'heal',
        amount: 25,
      });

      expect(result.success).toBe(true);
    });

    it('should validate move action', () => {
      const result = ActionSchema.safeParse({
        type: 'move',
        x: 10,
        y: 20,
      });

      expect(result.success).toBe(true);
    });

    it('should reject invalid action type', () => {
      const result = ActionSchema.safeParse({
        type: 'unknown',
      });

      expect(result.success).toBe(false);
    });
  });
});
