// src/utils/zodUtils.test.ts
import { describe, it, expect } from 'vitest';
import {
  requiredString,
  numericIdParam,
  uuidParamSchema,
  optionalBoolean,
  optionalNumeric,
  optionalDate,
  optionalString,
} from './zodUtils';

describe('Zod Utilities', () => {
  describe('requiredString', () => {
    const customMessage = 'This field is required and cannot be empty.';
    const schema = requiredString(customMessage);

    it('should pass for a valid non-empty string', () => {
      const result = schema.safeParse('hello world');
      expect(result.success).toBe(true);
      if (result.success) {
        expect(result.data).toBe('hello world');
      }
    });

    it('should fail for an empty string with the custom message', () => {
      const result = schema.safeParse('');
      expect(result.success).toBe(false);
      if (!result.success) {
        expect(result.error.issues[0].message).toBe(customMessage);
      }
    });

    it('should fail for a null value with the custom message', () => {
      const result = schema.safeParse(null);
      expect(result.success).toBe(false);
      if (!result.success) {
        expect(result.error.issues[0].message).toBe(customMessage);
      }
    });

    it('should fail for an undefined value with the custom message', () => {
      const result = schema.safeParse(undefined);
      expect(result.success).toBe(false);
      if (!result.success) {
        expect(result.error.issues[0].message).toBe(customMessage);
      }
    });

    it('should fail for a string containing only whitespace', () => {
      const result = schema.safeParse('   ');
      expect(result.success).toBe(false);
      if (!result.success) {
        expect(result.error.issues[0].message).toBe(customMessage);
      }
    });

    it('should trim whitespace from a valid string', () => {
      const result = schema.safeParse('  hello world  ');
      expect(result.success).toBe(true);
      if (result.success) {
        // The .trim() in the schema should remove leading/trailing whitespace.
        expect(result.data).toBe('hello world');
      }
    });

    it('should fail for a non-string value like a number with a Zod type error', () => {
      const result = schema.safeParse(123);
      expect(result.success).toBe(false);
      if (!result.success) {
        // z.string() will throw its own error message before min(1) is checked.
        expect(result.error.issues[0].message).toBe(
          'Invalid input: expected string, received number',
        );
      }
    });

    it('should fail for a non-string value like an object with a Zod type error', () => {
      const result = schema.safeParse({ a: 1 });
      expect(result.success).toBe(false);
      if (!result.success) {
        expect(result.error.issues[0].message).toBe(
          'Invalid input: expected string, received object',
        );
      }
    });
  });

  describe('numericIdParam', () => {
    const schema = numericIdParam('id');

    it('should pass for a valid numeric string in params', () => {
      const result = schema.safeParse({ params: { id: '123' } });
      expect(result.success).toBe(true);
      if (result.success) {
        expect(result.data.params.id).toBe(123);
      }
    });

    it('should pass for a valid number in params', () => {
      const result = schema.safeParse({ params: { id: 456 } });
      expect(result.success).toBe(true);
      if (result.success) {
        expect(result.data.params.id).toBe(456);
      }
    });

    it('should fail for a non-numeric string', () => {
      const result = schema.safeParse({ params: { id: 'abc' } });
      expect(result.success).toBe(false);
      if (!result.success) {
        expect(result.error.issues[0].message).toBe('Invalid input: expected number, received NaN');
      }
    });

    it('should fail for a negative number', () => {
      const result = schema.safeParse({ params: { id: -1 } });
      expect(result.success).toBe(false);
      if (!result.success) {
        expect(result.error.issues[0].message).toBe("Invalid ID for parameter 'id'. Must be a number.");
      }
    });

    it('should fail for a floating point number', () => {
      const result = schema.safeParse({ params: { id: 1.5 } });
      expect(result.success).toBe(false);
      if (!result.success) {
        expect(result.error.issues[0].message).toBe("Invalid ID for parameter 'id'. Must be a number.");
      }
    });

    it('should fail for zero', () => {
      const result = schema.safeParse({ params: { id: 0 } });
      expect(result.success).toBe(false);
      if (!result.success) {
        expect(result.error.issues[0].message).toBe("Invalid ID for parameter 'id'. Must be a number.");
      }
    });

    it('should use a custom error message if provided', () => {
      const customMessage = 'A valid numeric ID is required.';
      const customSchema = numericIdParam('id', customMessage);
      const result = customSchema.safeParse({ params: { id: -5 } });
      expect(result.success).toBe(false);
      if (!result.success) {
        expect(result.error.issues[0].message).toBe(customMessage);
      }
    });
  });

  describe('uuidParamSchema', () => {
    const customMessage = 'A valid UUID is required for the user ID.';
    const schema = uuidParamSchema('userId', customMessage);

    it('should pass for a valid UUID string', () => {
      const validUuid = '123e4567-e89b-12d3-a456-426614174000';
      const result = schema.safeParse({ params: { userId: validUuid } });
      expect(result.success).toBe(true);
    });

    it('should fail for an invalid UUID string', () => {
      const invalidUuid = 'not-a-uuid';
      const result = schema.safeParse({ params: { userId: invalidUuid } });
      expect(result.success).toBe(false);
      if (!result.success) {
        expect(result.error.issues[0].message).toBe(customMessage);
      }
    });

    it('should fail for a non-string value', () => {
      const result = schema.safeParse({ params: { userId: 12345 } });
      expect(result.success).toBe(false);
    });
  });

  describe('optionalNumeric', () => {
    it('should return the default value if input is undefined', () => {
      const schema = optionalNumeric({ default: 10 });
      const result = schema.safeParse(undefined);
      expect(result.success).toBe(true);
      if (result.success) {
        expect(result.data).toBe(10);
      }
    });

    it('should parse a valid numeric string', () => {
      const schema = optionalNumeric();
      const result = schema.safeParse('123.45');
      expect(result.success).toBe(true);
      if (result.success) {
        expect(result.data).toBe(123.45);
      }
    });

    it('should parse an empty string as 0', () => {
      const schema = optionalNumeric();
      const result = schema.safeParse('');
      expect(result.success).toBe(true);
      if (result.success) {
        expect(result.data).toBe(0);
      }
    });

    it('should parse a whitespace string as 0', () => {
      const schema = optionalNumeric();
      const result = schema.safeParse('   ');
      expect(result.success).toBe(true);
      if (result.success) {
        expect(result.data).toBe(0);
      }
    });

    it('should treat null as undefined, returning default value or undefined', () => {
      const schemaWithDefault = optionalNumeric({ default: 99 });
      const resultWithDefault = schemaWithDefault.safeParse(null);
      expect(resultWithDefault.success).toBe(true);
      if (resultWithDefault.success) {
        expect(resultWithDefault.data).toBe(99);
      }

      const schemaWithoutDefault = optionalNumeric();
      const resultWithoutDefault = schemaWithoutDefault.safeParse(null);
      expect(resultWithoutDefault.success).toBe(true);
      if (resultWithoutDefault.success) {
        expect(resultWithoutDefault.data).toBeUndefined();
      }
    });

    it('should fail for a non-numeric string', () => {
      const schema = optionalNumeric();
      const result = schema.safeParse('abc');
      expect(result.success).toBe(false);
    });

    it('should enforce integer constraint', () => {
      const schema = optionalNumeric({ integer: true });
      expect(schema.safeParse('123').success).toBe(true);
      const floatResult = schema.safeParse('123.45');
      expect(floatResult.success).toBe(false);
      if (!floatResult.success) expect(floatResult.error.issues[0].message).toBe('Invalid input: expected int, received number');
    });

    it('should enforce positive constraint', () => {
      const schema = optionalNumeric({ positive: true });
      expect(schema.safeParse('1').success).toBe(true);
      const zeroResult = schema.safeParse('0');
      expect(zeroResult.success).toBe(false);
      if (!zeroResult.success) {
        expect(zeroResult.error.issues[0].message).toBe('Too small: expected number to be >0');
      }
    });

    it('should enforce non-negative constraint', () => {
      const schema = optionalNumeric({ nonnegative: true });
      expect(schema.safeParse('0').success).toBe(true);
      const negativeResult = schema.safeParse('-1');
      expect(negativeResult.success).toBe(false);
      if (!negativeResult.success) {
        expect(negativeResult.error.issues[0].message).toBe('Too small: expected number to be >=0');
      }
    });

    it('should enforce min and max constraints', () => {
      const schema = optionalNumeric({ min: 10, max: 20 });
      expect(schema.safeParse('15').success).toBe(true);
      const tooSmallResult = schema.safeParse('9');
      expect(tooSmallResult.success).toBe(false);
      if (!tooSmallResult.success) {
        expect(tooSmallResult.error.issues[0].message).toBe('Too small: expected number to be >=10');
      }
      const tooLargeResult = schema.safeParse('21');
      expect(tooLargeResult.success).toBe(false);
      if (!tooLargeResult.success) {
        expect(tooLargeResult.error.issues[0].message).toBe('Too big: expected number to be <=20');
      }
    });
  });

  describe('optionalDate', () => {
    const schema = optionalDate('Invalid date format');

    it('should pass for a valid YYYY-MM-DD date string', () => {
      const result = schema.safeParse('2023-12-25');
      expect(result.success).toBe(true);
      if (result.success) {
        expect(result.data).toBe('2023-12-25');
      }
    });

    it('should pass for undefined (optional)', () => {
      expect(schema.safeParse(undefined).success).toBe(true);
    });

    it('should fail for an invalid date string', () => {
      expect(schema.safeParse('not-a-date').success).toBe(false);
    });
  });

  describe('optionalBoolean', () => {
    it('should return the default value if input is undefined', () => {
      const schema = optionalBoolean({ default: true });
      const result = schema.safeParse(undefined);
      expect(result.success).toBe(true);
      if (result.success) {
        expect(result.data).toBe(true);
      }
    });

    it('should return undefined if input is undefined and no default is set', () => {
      const schema = optionalBoolean();
      const result = schema.safeParse(undefined);
      expect(result.success).toBe(true);
      if (result.success) {
        expect(result.data).toBeUndefined();
      }
    });

    it('should parse "true" string as true', () => {
      const schema = optionalBoolean();
      const result = schema.safeParse('true');
      expect(result.success).toBe(true);
      if (result.success) {
        expect(result.data).toBe(true);
      }
    });

    it('should parse "false" string as false', () => {
      const schema = optionalBoolean();
      const result = schema.safeParse('false');
      expect(result.success).toBe(true);
      if (result.success) {
        expect(result.data).toBe(false);
      }
    });

    it('should parse "1" as true', () => {
      const schema = optionalBoolean();
      const result = schema.safeParse('1');
      expect(result.success).toBe(true);
      if (result.success) {
        expect(result.data).toBe(true);
      }
    });

    it('should parse "0" as false', () => {
      const schema = optionalBoolean();
      const result = schema.safeParse('0');
      expect(result.success).toBe(true);
      if (result.success) {
        expect(result.data).toBe(false);
      }
    });

    it('should fail for other strings', () => {
      const schema = optionalBoolean();
      const result = schema.safeParse('not-a-boolean');
      expect(result.success).toBe(false);
    });

    it('should handle null input, returning default or undefined', () => {
      const schemaWithDefault = optionalBoolean({ default: false });
      const resultWithDefault = schemaWithDefault.safeParse(null);
      expect(resultWithDefault.success).toBe(true);
      if (resultWithDefault.success) {
        expect(resultWithDefault.data).toBe(false);
      }

      const schemaWithoutDefault = optionalBoolean();
      const resultWithoutDefault = schemaWithoutDefault.safeParse(null);
      expect(resultWithoutDefault.success).toBe(true);
      if (resultWithoutDefault.success) {
        expect(resultWithoutDefault.data).toBeUndefined();
      }
    });

    it('should handle empty string input, returning default or undefined', () => {
      const schemaWithDefault = optionalBoolean({ default: true });
      const resultWithDefault = schemaWithDefault.safeParse('');
      expect(resultWithDefault.success).toBe(true);
      if (resultWithDefault.success) {
        expect(resultWithDefault.data).toBe(true);
      }

      const schemaWithoutDefault = optionalBoolean();
      const resultWithoutDefault = schemaWithoutDefault.safeParse('');
      expect(resultWithoutDefault.success).toBe(true);
      if (resultWithoutDefault.success) {
        expect(resultWithoutDefault.data).toBeUndefined();
      }
    });

    it('should pass for an actual boolean value', () => {
      const schema = optionalBoolean();
      expect(schema.safeParse(true).success).toBe(true);
      expect(schema.safeParse(false).success).toBe(true);
    });
  });

  describe('optionalString', () => {
    const schema = optionalString();

    it('should pass for a valid string', () => {
      const result = schema.safeParse('hello');
      expect(result.success).toBe(true);
      if (result.success) {
        expect(result.data).toBe('hello');
      }
    });

    it('should pass for an empty string', () => {
      const result = schema.safeParse('');
      expect(result.success).toBe(true);
      if (result.success) {
        expect(result.data).toBe('');
      }
    });

    it('should pass for undefined and return undefined', () => {
      const result = schema.safeParse(undefined);
      expect(result.success).toBe(true);
      if (result.success) {
        expect(result.data).toBeUndefined();
      }
    });

    it('should pass for null and return undefined', () => {
      const result = schema.safeParse(null);
      expect(result.success).toBe(true);
      if (result.success) {
        expect(result.data).toBeUndefined();
      }
    });

    it('should fail for a non-string value like a number', () => {
      const result = schema.safeParse(123);
      expect(result.success).toBe(false);
      if (!result.success) {
        expect(result.error.issues[0].message).toBe(
          'Invalid input: expected string, received number',
        );
      }
    });
  });
});
