import { describe, it, expect } from 'vitest';
import { renderHook, act } from '@testing-library/react';
import {
  useFormValidation,
  required,
  email,
  minLength,
  maxLength,
  minValue,
  maxValue,
  pattern,
  url,
  matches,
  phone,
} from '../useFormValidation';

describe('useFormValidation', () => {
  describe('hook functionality', () => {
    it('initializes with no errors', () => {
      const { result } = renderHook(() => useFormValidation({}));
      expect(result.current.errors).toEqual({});
      expect(result.current.isValid).toBe(true);
    });

    it('validates form and returns errors', () => {
      const schema = {
        name: [required('Name is required')],
      };
      const { result } = renderHook(() => useFormValidation(schema));

      act(() => {
        result.current.validateForm({ name: '' });
      });

      expect(result.current.errors.name).toBe('Name is required');
      expect(result.current.isValid).toBe(false);
    });

    it('validates single field', () => {
      const schema = {
        email: [email('Invalid email')],
      };
      const { result } = renderHook(() => useFormValidation(schema));

      const error = result.current.validateField('email', 'invalid');
      expect(error).toBe('Invalid email');
    });

    it('returns undefined for valid field', () => {
      const schema = {
        email: [email('Invalid email')],
      };
      const { result } = renderHook(() => useFormValidation(schema));

      const error = result.current.validateField('email', 'test@example.com');
      expect(error).toBeUndefined();
    });

    it('sets error manually', () => {
      const { result } = renderHook(() => useFormValidation({}));

      act(() => {
        result.current.setError('field', 'Custom error');
      });

      expect(result.current.errors.field).toBe('Custom error');
    });

    it('clears single error', () => {
      const { result } = renderHook(() => useFormValidation({}));

      act(() => {
        result.current.setError('field', 'Error');
        result.current.clearError('field');
      });

      expect(result.current.errors.field).toBeUndefined();
    });

    it('clears all errors', () => {
      const { result } = renderHook(() => useFormValidation({}));

      act(() => {
        result.current.setError('field1', 'Error 1');
        result.current.setError('field2', 'Error 2');
        result.current.clearAllErrors();
      });

      expect(result.current.errors).toEqual({});
    });

    it('getError returns correct error', () => {
      const { result } = renderHook(() => useFormValidation({}));

      act(() => {
        result.current.setError('field', 'Test error');
      });

      expect(result.current.getError('field')).toBe('Test error');
    });

    it('hasError returns true when error exists', () => {
      const { result } = renderHook(() => useFormValidation({}));

      act(() => {
        result.current.setError('field', 'Error');
      });

      expect(result.current.hasError('field')).toBe(true);
    });

    it('hasError returns false when no error', () => {
      const { result } = renderHook(() => useFormValidation({}));
      expect(result.current.hasError('field')).toBe(false);
    });
  });

  describe('required validator', () => {
    it('returns error for undefined', () => {
      const validator = required('Required');
      expect(validator(undefined)).toBe('Required');
    });

    it('returns error for null', () => {
      const validator = required('Required');
      expect(validator(null)).toBe('Required');
    });

    it('returns error for empty string', () => {
      const validator = required('Required');
      expect(validator('')).toBe('Required');
    });

    it('returns error for empty array', () => {
      const validator = required('Required');
      expect(validator([])).toBe('Required');
    });

    it('returns undefined for valid value', () => {
      const validator = required('Required');
      expect(validator('value')).toBeUndefined();
    });

    it('uses default message', () => {
      const validator = required();
      expect(validator('')).toBe('This field is required');
    });
  });

  describe('email validator', () => {
    it('returns error for invalid email', () => {
      const validator = email('Invalid');
      expect(validator('notanemail')).toBe('Invalid');
    });

    it('returns undefined for valid email', () => {
      const validator = email('Invalid');
      expect(validator('test@example.com')).toBeUndefined();
    });

    it('returns undefined for empty value', () => {
      const validator = email('Invalid');
      expect(validator('')).toBeUndefined();
    });
  });

  describe('minLength validator', () => {
    it('returns error when too short', () => {
      const validator = minLength(5, 'Too short');
      expect(validator('ab')).toBe('Too short');
    });

    it('returns undefined when long enough', () => {
      const validator = minLength(5, 'Too short');
      expect(validator('abcde')).toBeUndefined();
    });

    it('uses default message', () => {
      const validator = minLength(5);
      expect(validator('ab')).toBe('Must be at least 5 characters');
    });
  });

  describe('maxLength validator', () => {
    it('returns error when too long', () => {
      const validator = maxLength(3, 'Too long');
      expect(validator('abcd')).toBe('Too long');
    });

    it('returns undefined when short enough', () => {
      const validator = maxLength(3, 'Too long');
      expect(validator('abc')).toBeUndefined();
    });

    it('uses default message', () => {
      const validator = maxLength(3);
      expect(validator('abcd')).toBe('Must be at most 3 characters');
    });
  });

  describe('minValue validator', () => {
    it('returns error when below min', () => {
      const validator = minValue(10, 'Too small');
      expect(validator(5)).toBe('Too small');
    });

    it('returns undefined when at or above min', () => {
      const validator = minValue(10, 'Too small');
      expect(validator(10)).toBeUndefined();
    });

    it('returns undefined for null/undefined', () => {
      const validator = minValue(10);
      expect(validator(undefined as unknown as number)).toBeUndefined();
    });
  });

  describe('maxValue validator', () => {
    it('returns error when above max', () => {
      const validator = maxValue(10, 'Too big');
      expect(validator(15)).toBe('Too big');
    });

    it('returns undefined when at or below max', () => {
      const validator = maxValue(10, 'Too big');
      expect(validator(10)).toBeUndefined();
    });
  });

  describe('pattern validator', () => {
    it('returns error when pattern does not match', () => {
      const validator = pattern(/^[a-z]+$/, 'Letters only');
      expect(validator('abc123')).toBe('Letters only');
    });

    it('returns undefined when pattern matches', () => {
      const validator = pattern(/^[a-z]+$/, 'Letters only');
      expect(validator('abc')).toBeUndefined();
    });
  });

  describe('url validator', () => {
    it('returns error for invalid URL', () => {
      const validator = url('Invalid URL');
      expect(validator('not-a-url')).toBe('Invalid URL');
    });

    it('returns undefined for valid URL', () => {
      const validator = url('Invalid URL');
      expect(validator('https://example.com')).toBeUndefined();
    });

    it('returns undefined for empty value', () => {
      const validator = url('Invalid URL');
      expect(validator('')).toBeUndefined();
    });
  });

  describe('matches validator', () => {
    it('returns error when fields do not match', () => {
      const validator = matches('password', 'Must match');
      expect(validator('abc', { password: 'xyz' })).toBe('Must match');
    });

    it('returns undefined when fields match', () => {
      const validator = matches('password', 'Must match');
      expect(validator('abc', { password: 'abc' })).toBeUndefined();
    });

    it('returns undefined when no form data', () => {
      const validator = matches('password');
      expect(validator('abc')).toBeUndefined();
    });
  });

  describe('phone validator', () => {
    it('returns error for invalid phone', () => {
      const validator = phone('Invalid phone');
      expect(validator('abc')).toBe('Invalid phone');
    });

    it('returns undefined for valid phone', () => {
      const validator = phone('Invalid phone');
      // Use a phone format that matches the regex: /^[+]?[(]?[0-9]{1,4}[)]?[-\s./0-9]*$/
      expect(validator('+15551234567')).toBeUndefined();
    });

    it('returns undefined for empty value', () => {
      const validator = phone('Invalid phone');
      expect(validator('')).toBeUndefined();
    });
  });
});
