/**
 * Tests for NavigationContext - navigation-context.tsx
 *
 * Tests navigation state transitions, custom navigation providers,
 * and Link component integration.
 */

import { describe, it, expect, vi } from 'vitest'
import { render, screen, fireEvent } from '@testing-library/react'
import {
  NavigationProvider,
  useNavigation,
  type NavigationContextValue,
} from '../../context/navigation-context'
import type { NavigationProvider as NavigationProviderInterface, LinkProps } from '../../types'
import type { ComponentType } from 'react'

// Helper component to consume navigation context
function NavigationConsumer({ onNavigation }: { onNavigation: (nav: NavigationContextValue) => void }) {
  const nav = useNavigation()
  onNavigation(nav)
  return (
    <div>
      <span data-testid="current-path">{nav.getCurrentPath()}</span>
      <nav.Link href="/test" className="test-link">
        Test Link
      </nav.Link>
      <button data-testid="navigate-btn" onClick={() => nav.navigate('/dashboard')}>
        Navigate
      </button>
      <button data-testid="navigate-replace-btn" onClick={() => nav.navigate('/settings', { replace: true })}>
        Navigate Replace
      </button>
    </div>
  )
}

function createMockNavigationProvider(
  overrides: Partial<NavigationProviderInterface> = {}
): NavigationProviderInterface {
  const MockLink: ComponentType<LinkProps> = ({ href, children, className }) => (
    <a href={href} className={className} data-testid="mock-link">
      {children}
    </a>
  )

  return {
    navigate: vi.fn(),
    getCurrentPath: vi.fn().mockReturnValue('/'),
    LinkComponent: MockLink,
    ...overrides,
  }
}

describe('NavigationContext', () => {
  describe('NavigationProvider', () => {
    it('should provide navigation functions to children', () => {
      const mockProvider = createMockNavigationProvider({
        getCurrentPath: vi.fn().mockReturnValue('/home'),
      })
      let navValue: NavigationContextValue | undefined

      render(
        <NavigationProvider navigationProvider={mockProvider}>
          <NavigationConsumer onNavigation={(nav) => { navValue = nav }} />
        </NavigationProvider>
      )

      expect(navValue).toBeDefined()
      expect(navValue!.navigate).toBeDefined()
      expect(navValue!.getCurrentPath).toBeDefined()
      expect(navValue!.Link).toBeDefined()
      expect(screen.getByTestId('current-path').textContent).toBe('/home')
    })

    it('should call navigate with path and options', () => {
      const mockNavigate = vi.fn()
      const mockProvider = createMockNavigationProvider({ navigate: mockNavigate })

      render(
        <NavigationProvider navigationProvider={mockProvider}>
          <NavigationConsumer onNavigation={() => {}} />
        </NavigationProvider>
      )

      // Click navigate button
      fireEvent.click(screen.getByTestId('navigate-btn'))
      expect(mockNavigate).toHaveBeenCalledWith('/dashboard')

      // Click navigate with replace
      fireEvent.click(screen.getByTestId('navigate-replace-btn'))
      expect(mockNavigate).toHaveBeenCalledWith('/settings', { replace: true })
    })

    it('should render custom Link component', () => {
      const CustomLink: ComponentType<LinkProps> = ({ href, children, className }) => (
        <a href={href} className={className} data-testid="custom-link">
          Custom: {children}
        </a>
      )
      const mockProvider = createMockNavigationProvider({ LinkComponent: CustomLink })

      render(
        <NavigationProvider navigationProvider={mockProvider}>
          <NavigationConsumer onNavigation={() => {}} />
        </NavigationProvider>
      )

      const link = screen.getByTestId('custom-link')
      expect(link).toBeDefined()
      expect(link.getAttribute('href')).toBe('/test')
      expect(link.className).toContain('test-link')
      expect(link.textContent).toBe('Custom: Test Link')
    })

    it('should use default navigation provider when none provided', () => {
      // Mock window.location
      const originalLocation = window.location
      const mockLocation = {
        ...originalLocation,
        pathname: '/default-path',
        href: '/default-path',
      }
      Object.defineProperty(window, 'location', {
        value: mockLocation,
        writable: true,
      })

      render(
        <NavigationProvider>
          <NavigationConsumer onNavigation={() => {}} />
        </NavigationProvider>
      )

      expect(screen.getByTestId('current-path').textContent).toBe('/default-path')

      // Restore
      Object.defineProperty(window, 'location', {
        value: originalLocation,
        writable: true,
      })
    })

    it('should provide default Link component that renders anchor tags', () => {
      render(
        <NavigationProvider>
          <NavigationConsumer onNavigation={() => {}} />
        </NavigationProvider>
      )

      // Default Link renders as <a> tag
      const links = screen.getAllByRole('link')
      const testLink = links.find(link => link.textContent === 'Test Link')
      expect(testLink).toBeDefined()
      expect(testLink?.getAttribute('href')).toBe('/test')
    })
  })

  describe('useNavigation', () => {
    it('should throw error when used outside NavigationProvider', () => {
      const consoleError = vi.spyOn(console, 'error').mockImplementation(() => {})

      expect(() => {
        render(<NavigationConsumer onNavigation={() => {}} />)
      }).toThrow('useNavigation must be used within a NavigationProvider')

      consoleError.mockRestore()
    })
  })

  describe('navigation state transitions', () => {
    it('should track path changes through getCurrentPath', () => {
      let currentPath = '/initial'
      const mockProvider = createMockNavigationProvider({
        getCurrentPath: vi.fn(() => currentPath),
        navigate: vi.fn((to: string) => {
          currentPath = to
        }),
      })

      const { rerender } = render(
        <NavigationProvider navigationProvider={mockProvider}>
          <NavigationConsumer onNavigation={() => {}} />
        </NavigationProvider>
      )

      expect(screen.getByTestId('current-path').textContent).toBe('/initial')

      // Simulate navigation
      fireEvent.click(screen.getByTestId('navigate-btn'))

      // Re-render to see updated path
      rerender(
        <NavigationProvider navigationProvider={mockProvider}>
          <NavigationConsumer onNavigation={() => {}} />
        </NavigationProvider>
      )

      expect(screen.getByTestId('current-path').textContent).toBe('/dashboard')
    })

    it('should pass state to navigate when provided', () => {
      const mockNavigate = vi.fn()
      const mockProvider = createMockNavigationProvider({ navigate: mockNavigate })

      function StateNavigator() {
        const nav = useNavigation()
        return (
          <button
            data-testid="nav-with-state"
            onClick={() => nav.navigate('/page', { replace: false, state: { from: 'home' } })}
          >
            Nav with State
          </button>
        )
      }

      render(
        <NavigationProvider navigationProvider={mockProvider}>
          <StateNavigator />
        </NavigationProvider>
      )

      fireEvent.click(screen.getByTestId('nav-with-state'))
      expect(mockNavigate).toHaveBeenCalledWith('/page', { replace: false, state: { from: 'home' } })
    })
  })

  describe('Link component integration', () => {
    it('should render Link with all props passed through', () => {
      const MockLink: ComponentType<LinkProps> = ({ href, children, className }) => (
        <a href={href} className={className} data-custom="true">
          {children}
        </a>
      )
      const mockProvider = createMockNavigationProvider({ LinkComponent: MockLink })

      function LinkRenderer() {
        const { Link } = useNavigation()
        return (
          <Link href="/about" className="nav-link">
            About Us
          </Link>
        )
      }

      render(
        <NavigationProvider navigationProvider={mockProvider}>
          <LinkRenderer />
        </NavigationProvider>
      )

      const link = screen.getByRole('link')
      expect(link.getAttribute('href')).toBe('/about')
      expect(link.className).toContain('nav-link')
      expect(link.textContent).toBe('About Us')
    })

    it('should support nested children in Link', () => {
      const mockProvider = createMockNavigationProvider()

      function NestedLinkRenderer() {
        const { Link } = useNavigation()
        return (
          <Link href="/profile" className="profile-link">
            <span data-testid="icon">*</span>
            <span data-testid="text">Profile</span>
          </Link>
        )
      }

      render(
        <NavigationProvider navigationProvider={mockProvider}>
          <NestedLinkRenderer />
        </NavigationProvider>
      )

      expect(screen.getByTestId('icon')).toBeDefined()
      expect(screen.getByTestId('text')).toBeDefined()
    })
  })
})
