/**
 * PageNotFoundNotification Component Tests
 *
 * Tests the notification toast that appears when users are redirected
 * from an invalid dealer page path. Covers: visibility, URL cleanup,
 * auto-dismiss behavior, manual dismiss, and accessibility.
 */

import { render, screen, fireEvent, act } from '@testing-library/react';
import { PageNotFoundNotification } from '../PageNotFoundNotification';

// Mock next/navigation hooks
const mockGet = jest.fn();
const mockPathname = '/dealers/test-dealer';

jest.mock('next/navigation', () => ({
  useSearchParams: () => ({
    get: mockGet,
  }),
  usePathname: () => mockPathname,
}));

// Mock window.history.replaceState
const mockReplaceState = jest.fn();
Object.defineProperty(window, 'history', {
  value: {
    replaceState: mockReplaceState,
  },
  writable: true,
});

describe('PageNotFoundNotification', () => {
  beforeEach(() => {
    jest.useFakeTimers();
    mockGet.mockReset();
    mockReplaceState.mockReset();
  });

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

  describe('Visibility', () => {
    it('shows notification when notfound=1 is present', () => {
      mockGet.mockReturnValue('1');

      render(<PageNotFoundNotification />);

      expect(screen.getByRole('alert')).toBeInTheDocument();
      expect(
        screen.getByText(/the page you requested was not found/i)
      ).toBeInTheDocument();
    });

    it('does not show notification when notfound param is absent', () => {
      mockGet.mockReturnValue(null);

      render(<PageNotFoundNotification />);

      expect(screen.queryByRole('alert')).not.toBeInTheDocument();
    });

    it('does not show notification when notfound has different value', () => {
      mockGet.mockReturnValue('0');

      render(<PageNotFoundNotification />);

      expect(screen.queryByRole('alert')).not.toBeInTheDocument();
    });
  });

  describe('URL Cleanup', () => {
    it('cleans up URL by removing query parameter on mount', () => {
      mockGet.mockReturnValue('1');

      render(<PageNotFoundNotification />);

      expect(mockReplaceState).toHaveBeenCalledWith({}, '', mockPathname);
    });

    it('does not call replaceState when notification is not shown', () => {
      mockGet.mockReturnValue(null);

      render(<PageNotFoundNotification />);

      expect(mockReplaceState).not.toHaveBeenCalled();
    });
  });

  describe('Auto-Dismiss', () => {
    it('auto-dismisses after 5 seconds', async () => {
      mockGet.mockReturnValue('1');

      render(<PageNotFoundNotification />);

      expect(screen.getByRole('alert')).toBeInTheDocument();

      // Advance past auto-dismiss timeout (5s) + animation (300ms)
      act(() => {
        jest.advanceTimersByTime(5000);
      });

      // Wait for exit animation
      act(() => {
        jest.advanceTimersByTime(300);
      });

      expect(screen.queryByRole('alert')).not.toBeInTheDocument();
    });

    it('applies exit animation class before disappearing', () => {
      mockGet.mockReturnValue('1');

      const { container } = render(<PageNotFoundNotification />);

      // The outer wrapper div has the animation classes
      const notification = container.firstChild as HTMLElement;
      expect(notification.className).toContain('animate-notification-slide-down');

      // Advance to trigger auto-dismiss
      act(() => {
        jest.advanceTimersByTime(5000);
      });

      // Should now have exit animation class
      expect(notification.className).toContain('animate-notification-slide-up');
    });
  });

  describe('Manual Dismiss', () => {
    it('dismisses immediately when dismiss button clicked', async () => {
      mockGet.mockReturnValue('1');

      render(<PageNotFoundNotification />);

      const dismissButton = screen.getByRole('button', {
        name: /dismiss notification/i,
      });
      fireEvent.click(dismissButton);

      // Wait for exit animation
      act(() => {
        jest.advanceTimersByTime(300);
      });

      expect(screen.queryByRole('alert')).not.toBeInTheDocument();
    });

    it('clears auto-dismiss timer when manually dismissed', () => {
      mockGet.mockReturnValue('1');

      render(<PageNotFoundNotification />);

      // Dismiss manually at 2 seconds
      act(() => {
        jest.advanceTimersByTime(2000);
      });

      const dismissButton = screen.getByRole('button', {
        name: /dismiss notification/i,
      });
      fireEvent.click(dismissButton);

      // Wait for animation
      act(() => {
        jest.advanceTimersByTime(300);
      });

      expect(screen.queryByRole('alert')).not.toBeInTheDocument();

      // Advance more time - should not throw or cause issues
      act(() => {
        jest.advanceTimersByTime(5000);
      });

      // Still gone
      expect(screen.queryByRole('alert')).not.toBeInTheDocument();
    });
  });

  describe('Accessibility', () => {
    it('has proper role and aria-live attributes', () => {
      mockGet.mockReturnValue('1');

      render(<PageNotFoundNotification />);

      const alert = screen.getByRole('alert');
      expect(alert).toHaveAttribute('aria-live', 'polite');
    });

    it('dismiss button has accessible label', () => {
      mockGet.mockReturnValue('1');

      render(<PageNotFoundNotification />);

      const dismissButton = screen.getByRole('button', {
        name: /dismiss notification/i,
      });
      expect(dismissButton).toHaveAttribute('aria-label', 'Dismiss notification');
    });
  });

  describe('Styling', () => {
    it('renders with correct base classes', () => {
      mockGet.mockReturnValue('1');

      const { container } = render(<PageNotFoundNotification />);

      const wrapper = container.firstChild as HTMLElement;
      expect(wrapper).toHaveClass('fixed');
      expect(wrapper).toHaveClass('z-[9999]');
    });

    it('includes warning icon', () => {
      mockGet.mockReturnValue('1');

      render(<PageNotFoundNotification />);

      expect(screen.getByText('!')).toBeInTheDocument();
    });
  });

  describe('Edge Cases', () => {
    it('notification survives URL cleanup', () => {
      mockGet.mockReturnValue('1');

      render(<PageNotFoundNotification />);

      // URL was cleaned
      expect(mockReplaceState).toHaveBeenCalled();

      // But notification still visible
      expect(screen.getByRole('alert')).toBeInTheDocument();

      // Until auto-dismiss
      act(() => {
        jest.advanceTimersByTime(5300);
      });

      expect(screen.queryByRole('alert')).not.toBeInTheDocument();
    });

    it('cleans up exit animation timer on unmount', () => {
      mockGet.mockReturnValue('1');

      const { unmount } = render(<PageNotFoundNotification />);

      // Click dismiss to start exit animation
      const dismissButton = screen.getByRole('button', {
        name: /dismiss notification/i,
      });
      fireEvent.click(dismissButton);

      // Unmount before the 300ms animation completes
      // This should NOT cause "Can't perform a React state update on an unmounted component"
      unmount();

      // Advance past the animation timeout - should not throw
      act(() => {
        jest.advanceTimersByTime(500);
      });

      // If we get here without errors, the cleanup worked
      expect(true).toBe(true);
    });
  });
});
