/**
 * Enhanced useValidation Hook Tests
 *
 * Additional comprehensive tests for the useValidation custom hook
 * These tests focus on hook behavior, edge cases, and integration scenarios
 */

import React from 'react';
import { render, screen, act, waitFor } from '@testing-library/react';
import '@testing-library/jest-dom';
import { useValidation } from '../useValidation';

// Test wrapper component
const TestComponent = ({ content, variant, options, onStateChange }) => {
  const validationState = useValidation(content, variant, options);

  // Effect to notify parent of validation state changes
  // Note: In production, onStateChange should be wrapped with useCallback
  // to avoid unnecessary re-runs. In tests, inline functions are acceptable.
  React.useEffect(() => {
    if (onStateChange) {
      onStateChange(validationState);
    }
  }, [validationState, onStateChange]);

  return (
    <div>
      <div data-testid="is-validating">{String(validationState.isValidating)}</div>
      <div data-testid="is-valid">{String(validationState.isValid)}</div>
      <div data-testid="is-secure">{String(validationState.isSecure)}</div>
      <div data-testid="is-clean">{String(validationState.isClean())}</div>
      <div data-testid="has-errors">{String(validationState.hasErrors)}</div>
      <div data-testid="has-warnings">{String(validationState.hasWarnings)}</div>
      <div data-testid="has-security-issues">{String(validationState.hasSecurityIssues)}</div>
      <div data-testid="total-errors">{validationState.summary.totalErrors}</div>
      <div data-testid="total-warnings">{validationState.summary.totalWarnings}</div>
      <div data-testid="total-info">{validationState.summary.totalInfo}</div>
      <div data-testid="html-errors-count">{validationState.htmlErrors.length}</div>
      <div data-testid="css-errors-count">{validationState.cssErrors.length}</div>
      <div data-testid="security-issues-count">{validationState.securityIssues.length}</div>
      <div data-testid="sanitization-warnings-count">{validationState.sanitizationWarnings.length}</div>
      <div data-testid="last-validated">{validationState.lastValidated ? 'validated' : 'not-validated'}</div>
    </div>
  );
};

describe('Enhanced useValidation Hook Tests', () => {
  // Use fake timers for debouncing tests
  beforeEach(() => {
    jest.useFakeTimers();
    jest.clearAllMocks();
  });

  afterEach(() => {
    jest.runOnlyPendingTimers();
    jest.useRealTimers();
  });

  describe('Hook Structure and API', () => {
    it('returns all expected properties and methods', async () => {
      let validationState;

      render(<TestComponent
        content="<div>test</div>"
        onStateChange={(state) => { validationState = state; }}
      />);

      await act(async () => {
        jest.advanceTimersByTime(500);
        await Promise.resolve();
      });

      // Check all expected properties exist
      expect(validationState).toHaveProperty('isValidating');
      expect(validationState).toHaveProperty('lastValidated');
      expect(validationState).toHaveProperty('htmlErrors');
      expect(validationState).toHaveProperty('htmlWarnings');
      expect(validationState).toHaveProperty('htmlInfo');
      expect(validationState).toHaveProperty('cssErrors');
      expect(validationState).toHaveProperty('cssWarnings');
      expect(validationState).toHaveProperty('cssInfo');
      expect(validationState).toHaveProperty('securityIssues');
      expect(validationState).toHaveProperty('sanitizationWarnings');
      expect(validationState).toHaveProperty('isValid');
      expect(validationState).toHaveProperty('isSecure');
      expect(validationState).toHaveProperty('summary');

      // Check all expected methods exist
      expect(typeof validationState.validateContent).toBe('function');
      expect(typeof validationState.forceValidation).toBe('function');
      expect(typeof validationState.clearValidation).toBe('function');
      expect(typeof validationState.getErrorsBySeverity).toBe('function');
      expect(typeof validationState.getErrorsBySource).toBe('function');
      expect(typeof validationState.getAllIssues).toBe('function');
      expect(typeof validationState.isClean).toBe('function');

      // Check computed properties
      expect(validationState).toHaveProperty('hasErrors');
      expect(validationState).toHaveProperty('hasWarnings');
      expect(validationState).toHaveProperty('hasSecurityIssues');
    });

    it('has correct initial state structure', () => {
      let validationState;

      render(<TestComponent
        content=""
        onStateChange={(state) => { validationState = state; }}
      />);

      expect(validationState.isValidating).toBe(false);
      expect(validationState.lastValidated).toBeNull();
      expect(Array.isArray(validationState.htmlErrors)).toBe(true);
      expect(Array.isArray(validationState.htmlWarnings)).toBe(true);
      expect(Array.isArray(validationState.htmlInfo)).toBe(true);
      expect(Array.isArray(validationState.cssErrors)).toBe(true);
      expect(Array.isArray(validationState.cssWarnings)).toBe(true);
      expect(Array.isArray(validationState.cssInfo)).toBe(true);
      expect(Array.isArray(validationState.securityIssues)).toBe(true);
      expect(Array.isArray(validationState.sanitizationWarnings)).toBe(true);
      expect(typeof validationState.isValid).toBe('boolean');
      expect(typeof validationState.isSecure).toBe('boolean');
      expect(typeof validationState.summary).toBe('object');
    });
  });

  describe('Advanced Configuration Options', () => {
    describe('Debouncing Behavior', () => {
      it('respects custom debounce timing', async () => {
        const options = { debounceMs: 1000 };

        render(<TestComponent content="<div>test</div>" options={options} />);

        // Should not validate immediately
        await act(async () => {
          jest.advanceTimersByTime(500);
          await Promise.resolve();
        });
        expect(screen.getByTestId('last-validated')).toHaveTextContent('not-validated');

        // Should validate after full debounce period
        await act(async () => {
          jest.advanceTimersByTime(500);
          await Promise.resolve();
        });
        expect(screen.getByTestId('last-validated')).toHaveTextContent('validated');
      });

      it('handles zero debounce correctly', async () => {
        const options = { debounceMs: 0 };

        render(<TestComponent content="<div>test</div>" options={options} />);

        await act(async () => {
          jest.advanceTimersByTime(0);
          await Promise.resolve();
        });

        expect(screen.getByTestId('last-validated')).toHaveTextContent('validated');
      });

      it('handles very long debounce periods', async () => {
        const options = { debounceMs: 5000 };

        render(<TestComponent content="<div>test</div>" options={options} />);

        await act(async () => {
          jest.advanceTimersByTime(4999);
          await Promise.resolve();
        });
        expect(screen.getByTestId('last-validated')).toHaveTextContent('not-validated');

        await act(async () => {
          jest.advanceTimersByTime(1);
          await Promise.resolve();
        });
        expect(screen.getByTestId('last-validated')).toHaveTextContent('validated');
      });
    });

    describe('Real-time Validation Control', () => {
      it('disables real-time validation when configured', async () => {
        const options = { enableRealTime: false };

        render(<TestComponent content="<div>test</div>" options={options} />);

        await act(async () => {
          jest.advanceTimersByTime(1000);
          await Promise.resolve();
        });

        expect(screen.getByTestId('last-validated')).toHaveTextContent('not-validated');
      });

      it('enables real-time validation by default', async () => {
        render(<TestComponent content="<div>test</div>" />);

        await act(async () => {
          jest.advanceTimersByTime(500);
          await Promise.resolve();
        });

        expect(screen.getByTestId('last-validated')).toHaveTextContent('validated');
      });
    });

    describe('Sanitization Configuration', () => {
      it('enables sanitization by default', async () => {
        let validationState;

        render(<TestComponent
          content="<div>test</div>"
          onStateChange={(state) => { validationState = state; }}
        />);

        await act(async () => {
          jest.advanceTimersByTime(500);
          await Promise.resolve();
        });

        // Should have called sanitization (verified by checking the state structure)
        expect(Array.isArray(validationState.sanitizationWarnings)).toBe(true);
      });

      it('disables sanitization when configured', async () => {
        const options = { enableSanitization: false };

        render(<TestComponent content="<div>test</div>" options={options} />);

        await act(async () => {
          jest.advanceTimersByTime(500);
          await Promise.resolve();
        });

        expect(screen.getByTestId('sanitization-warnings-count')).toHaveTextContent('0');
      });

      it('handles different security levels', async () => {
        const options = { securityLevel: 'strict' };

        render(<TestComponent content="<div>test</div>" options={options} />);

        await act(async () => {
          jest.advanceTimersByTime(500);
          await Promise.resolve();
        });

        expect(screen.getByTestId('is-validating')).toHaveTextContent('false');
      });
    });
  });

  describe('Variant Handling', () => {
    it('handles email variant', async () => {
      render(<TestComponent content="<div>test</div>" variant="email" />);

      await act(async () => {
        jest.advanceTimersByTime(500);
        await Promise.resolve();
      });

      expect(screen.getByTestId('last-validated')).toHaveTextContent('validated');
    });

    it('handles inapp variant', async () => {
      render(<TestComponent content="<div>test</div>" variant="inapp" />);

      await act(async () => {
        jest.advanceTimersByTime(500);
        await Promise.resolve();
      });

      expect(screen.getByTestId('last-validated')).toHaveTextContent('validated');
    });

    it('handles unknown variant gracefully', async () => {
      render(<TestComponent content="<div>test</div>" variant="unknown" />);

      await act(async () => {
        jest.advanceTimersByTime(500);
        await Promise.resolve();
      });

      expect(screen.getByTestId('last-validated')).toHaveTextContent('validated');
    });

    it('switches between variants correctly', async () => {
      const { rerender } = render(<TestComponent content="<div>test</div>" variant="email" />);

      await act(async () => {
        jest.advanceTimersByTime(500);
        await Promise.resolve();
      });

      expect(screen.getByTestId('last-validated')).toHaveTextContent('validated');

      rerender(<TestComponent content="<div>test</div>" variant="inapp" />);

      await act(async () => {
        jest.advanceTimersByTime(500);
        await Promise.resolve();
      });

      expect(screen.getByTestId('last-validated')).toHaveTextContent('validated');
    });
  });

  describe('Method Functionality', () => {
    describe('forceValidation', () => {
      it('bypasses debouncing', async () => {
        let validationState;
        const options = { debounceMs: 5000 };

        render(<TestComponent
          content="<div>test</div>"
          options={options}
          onStateChange={(state) => { validationState = state; }}
        />);

        // Should not validate yet due to long debounce
        await act(async () => {
          jest.advanceTimersByTime(1000);
          await Promise.resolve();
        });
        expect(screen.getByTestId('last-validated')).toHaveTextContent('not-validated');

        // Force validation should bypass debounce
        await act(async () => {
          validationState.forceValidation();
          await Promise.resolve();
        });
        expect(screen.getByTestId('last-validated')).toHaveTextContent('validated');
      });

      it('validates immediately', async () => {
        let validationState;

        render(<TestComponent
          content="<div>test</div>"
          onStateChange={(state) => { validationState = state; }}
        />);

        await act(async () => {
          validationState.forceValidation();
          await Promise.resolve();
        });

        expect(screen.getByTestId('last-validated')).toHaveTextContent('validated');
      });
    });

    describe('clearValidation', () => {
      it('resets validation state', async () => {
        let validationState;

        render(<TestComponent
          content="<div>test</div>"
          onStateChange={(state) => { validationState = state; }}
        />);

        // Wait for validation
        await act(async () => {
          jest.advanceTimersByTime(500);
          await Promise.resolve();
        });

        expect(screen.getByTestId('last-validated')).toHaveTextContent('validated');

        // Clear validation
        await act(async () => {
          validationState.clearValidation();
          await Promise.resolve();
        });

        expect(screen.getByTestId('last-validated')).toHaveTextContent('not-validated');
        expect(screen.getByTestId('is-valid')).toHaveTextContent('true');
        expect(screen.getByTestId('is-secure')).toHaveTextContent('true');
        expect(screen.getByTestId('total-errors')).toHaveTextContent('0');
        expect(screen.getByTestId('total-warnings')).toHaveTextContent('0');
      });

      it('cancels pending validation', async () => {
        let validationState;
        const options = { debounceMs: 2000 };

        render(<TestComponent
          content="<div>test</div>"
          options={options}
          onStateChange={(state) => { validationState = state; }}
        />);

        // Start validation but don't wait for completion
        await act(async () => {
          jest.advanceTimersByTime(1000);
          await Promise.resolve();
        });

        // Clear validation before debounce completes
        await act(async () => {
          validationState.clearValidation();
          await Promise.resolve();
        });

        // Advance past original debounce time
        await act(async () => {
          jest.advanceTimersByTime(2000);
          await Promise.resolve();
        });

        // Should still be not validated
        expect(screen.getByTestId('last-validated')).toHaveTextContent('not-validated');
      });
    });

    describe('validateContent', () => {
      it('validates new content with debouncing', async () => {
        let validationState;

        render(<TestComponent
          content="<div>initial</div>"
          onStateChange={(state) => { validationState = state; }}
        />);

        await act(async () => {
          validationState.validateContent('<div>new content</div>');
          jest.advanceTimersByTime(500);
          await Promise.resolve();
        });

        expect(screen.getByTestId('last-validated')).toHaveTextContent('validated');
      });

      it('skips validation for unchanged content', async () => {
        let validationState;
        const content = '<div>test content</div>';

        render(<TestComponent
          content={content}
          onStateChange={(state) => { validationState = state; }}
        />);

        // Initial validation
        await act(async () => {
          jest.advanceTimersByTime(500);
          await Promise.resolve();
        });

        expect(screen.getByTestId('last-validated')).toHaveTextContent('validated');

        // Clear the validated state to test skipping
        await act(async () => {
          validationState.clearValidation();
          await Promise.resolve();
        });

        // Validate same content again - should skip because content hasn't changed
        await act(async () => {
          validationState.validateContent(content);
          jest.advanceTimersByTime(500);
          await Promise.resolve();
        });

        // Should remain not-validated because content didn't change
        expect(screen.getByTestId('last-validated')).toHaveTextContent('not-validated');
      });
    });
  });

  describe('Utility Methods', () => {
    describe('getErrorsBySeverity', () => {
      it('returns function that filters by severity', async () => {
        let validationState;

        render(<TestComponent
          content="<div>test</div>"
          onStateChange={(state) => { validationState = state; }}
        />);

        await act(async () => {
          jest.advanceTimersByTime(500);
          await Promise.resolve();
        });

        const errors = validationState.getErrorsBySeverity('error');
        const warnings = validationState.getErrorsBySeverity('warning');
        const info = validationState.getErrorsBySeverity('info');

        expect(Array.isArray(errors)).toBe(true);
        expect(Array.isArray(warnings)).toBe(true);
        expect(Array.isArray(info)).toBe(true);
      });

      it('returns empty array for non-existent severity', async () => {
        let validationState;

        render(<TestComponent
          content="<div>test</div>"
          onStateChange={(state) => { validationState = state; }}
        />);

        await act(async () => {
          jest.advanceTimersByTime(500);
          await Promise.resolve();
        });

        const criticalErrors = validationState.getErrorsBySeverity('critical');
        expect(Array.isArray(criticalErrors)).toBe(true);
        expect(criticalErrors.length).toBe(0);
      });
    });

    describe('getErrorsBySource', () => {
      it('returns function that filters by source', async () => {
        let validationState;

        render(<TestComponent
          content="<div>test</div>"
          onStateChange={(state) => { validationState = state; }}
        />);

        await act(async () => {
          jest.advanceTimersByTime(500);
          await Promise.resolve();
        });

        const htmlErrors = validationState.getErrorsBySource('htmlhint');
        const cssErrors = validationState.getErrorsBySource('css-validator');

        expect(Array.isArray(htmlErrors)).toBe(true);
        expect(Array.isArray(cssErrors)).toBe(true);
      });

      it('returns empty array for non-existent source', async () => {
        let validationState;

        render(<TestComponent
          content="<div>test</div>"
          onStateChange={(state) => { validationState = state; }}
        />);

        await act(async () => {
          jest.advanceTimersByTime(500);
          await Promise.resolve();
        });

        const unknownErrors = validationState.getErrorsBySource('unknown-source');
        expect(Array.isArray(unknownErrors)).toBe(true);
        expect(unknownErrors.length).toBe(0);
      });
    });

    describe('getAllIssues', () => {
      it('returns combined and sorted issues', async () => {
        let validationState;

        render(<TestComponent
          content="<div>test</div>"
          onStateChange={(state) => { validationState = state; }}
        />);

        await act(async () => {
          jest.advanceTimersByTime(500);
          await Promise.resolve();
        });

        const allIssues = validationState.getAllIssues();

        expect(Array.isArray(allIssues)).toBe(true);
      });

      it('returns empty array when no issues exist', async () => {
        let validationState;

        render(<TestComponent
          content=""
          onStateChange={(state) => { validationState = state; }}
        />);

        await act(async () => {
          jest.advanceTimersByTime(500);
          await Promise.resolve();
        });

        const allIssues = validationState.getAllIssues();
        expect(Array.isArray(allIssues)).toBe(true);
      });
    });

    describe('isClean', () => {
      it('returns boolean indicating clean state', async () => {
        let validationState;

        render(<TestComponent
          content="<div>test</div>"
          onStateChange={(state) => { validationState = state; }}
        />);

        await act(async () => {
          jest.advanceTimersByTime(500);
          await Promise.resolve();
        });

        const isClean = validationState.isClean();
        expect(typeof isClean).toBe('boolean');
      });

      it('works consistently with computed property', async () => {
        let validationState;

        render(<TestComponent
          content="<div>test</div>"
          onStateChange={(state) => { validationState = state; }}
        />);

        await act(async () => {
          jest.advanceTimersByTime(500);
          await Promise.resolve();
        });

        const methodResult = validationState.isClean();
        const computedResult = screen.getByTestId('is-clean').textContent === 'true';

        expect(methodResult).toBe(computedResult);
      });
    });
  });

  describe('Edge Cases and Error Handling', () => {
    it('handles empty content gracefully', async () => {
      render(<TestComponent content="" />);

      await act(async () => {
        jest.advanceTimersByTime(500);
        await Promise.resolve();
      });

      expect(screen.getByTestId('is-valid')).toHaveTextContent('true');
      expect(screen.getByTestId('is-secure')).toHaveTextContent('true');
      expect(screen.getByTestId('total-errors')).toHaveTextContent('0');
    });

    it('handles null content gracefully', async () => {
      render(<TestComponent content={null} />);

      await act(async () => {
        jest.advanceTimersByTime(500);
        await Promise.resolve();
      });

      expect(screen.getByTestId('is-valid')).toHaveTextContent('true');
      expect(screen.getByTestId('is-secure')).toHaveTextContent('true');
    });

    it('handles undefined content gracefully', async () => {
      render(<TestComponent content={undefined} />);

      await act(async () => {
        jest.advanceTimersByTime(500);
        await Promise.resolve();
      });

      expect(screen.getByTestId('is-valid')).toHaveTextContent('true');
      expect(screen.getByTestId('is-secure')).toHaveTextContent('true');
    });

    it('handles very large content', async () => {
      const largeContent = '<div>' + 'a'.repeat(100000) + '</div>';

      render(<TestComponent content={largeContent} />);

      await act(async () => {
        jest.advanceTimersByTime(500);
        await Promise.resolve();
      });

      expect(screen.getByTestId('last-validated')).toHaveTextContent('validated');
    });

    it('handles rapid content changes', async () => {
      const { rerender } = render(<TestComponent content="<div>content1</div>" />);

      // Rapid changes
      await act(async () => {
        rerender(<TestComponent content="<div>content2</div>" />);
        rerender(<TestComponent content="<div>content3</div>" />);
        rerender(<TestComponent content="<div>final-content</div>" />);

        jest.advanceTimersByTime(500);
        await Promise.resolve();
      });

      expect(screen.getByTestId('last-validated')).toHaveTextContent('validated');
    });

    it('handles validation errors gracefully', async () => {
      // This test verifies that the hook handles validation errors without crashing
      // We can't easily mock the validation to throw errors, so we test the error handling path
      // by ensuring the hook completes validation even with potentially problematic content

      render(<TestComponent content="<div>potentially problematic content</div>" />);

      await act(async () => {
        jest.advanceTimersByTime(500);
        await Promise.resolve();
      });

      // Should handle any validation issues gracefully and complete the process
      expect(screen.getByTestId('is-validating')).toHaveTextContent('false');
      expect(screen.getByTestId('last-validated')).toHaveTextContent('validated');
    });
  });

  describe('Performance and Memory Management', () => {
    it('cleans up timers on unmount', async () => {
      const clearTimeoutSpy = jest.spyOn(global, 'clearTimeout');

      const { unmount } = render(<TestComponent content="<div>test</div>" />);

      unmount();

      expect(clearTimeoutSpy).toHaveBeenCalled();

      clearTimeoutSpy.mockRestore();
    });

    it('handles multiple simultaneous validations', async () => {
      render(
        <div>
          <TestComponent content="<div>content1</div>" />
          <TestComponent content="<div>content2</div>" />
          <TestComponent content="<div>content3</div>" />
        </div>
      );

      await act(async () => {
        jest.advanceTimersByTime(500);
        await Promise.resolve();
      });

      // All components should validate independently
      const validElements = screen.getAllByTestId('is-valid');
      expect(validElements).toHaveLength(3);
    });

    it('maintains performance with frequent updates', async () => {
      const { rerender } = render(<TestComponent content="<div>initial</div>" />);

      const startTime = performance.now();

      // Simulate frequent updates
      for (let i = 0; i < 20; i++) {
        rerender(<TestComponent content={`<div>content-${i}</div>`} />);
      }

      await act(async () => {
        jest.advanceTimersByTime(500);
        await Promise.resolve();
      });

      const endTime = performance.now();
      const duration = endTime - startTime;

      // Should complete reasonably quickly
      expect(duration).toBeLessThan(1000);
      expect(screen.getByTestId('last-validated')).toHaveTextContent('validated');
    });
  });
});