/**
 * Tests for AppContext - app-context.tsx
 *
 * Tests app configuration, navigation state, and user context.
 */

import { describe, it, expect, vi } from 'vitest'
import { render, screen } from '@testing-library/react'
import { AppProvider, useApp, type AppContextValue } from '../../context/app-context'
import type { AppConfig, NavGroup, UserIdentity } from '../../types'
import { Home, Settings, Users } from 'lucide-react'

// Helper component to consume app context
function AppConsumer({ onApp }: { onApp: (app: AppContextValue) => void }) {
  const app = useApp()
  onApp(app)
  return (
    <div>
      <span data-testid="app-name">{app.config.name}</span>
      <span data-testid="app-description">{app.config.description ?? 'none'}</span>
      <span data-testid="app-base-path">{app.config.basePath ?? 'none'}</span>
      <span data-testid="nav-count">{app.navigation.length}</span>
      <span data-testid="user-name">{app.user?.fullName ?? 'none'}</span>
      <span data-testid="is-loading">{app.isLoading?.toString() ?? 'undefined'}</span>
    </div>
  )
}

describe('AppContext', () => {
  describe('AppProvider', () => {
    it('should provide app config to children', () => {
      const config: AppConfig = {
        name: 'Admin Dashboard',
        description: 'Manage your application',
        basePath: '/admin',
      }

      let appValue: AppContextValue | undefined

      render(
        <AppProvider config={config}>
          <AppConsumer onApp={(app) => { appValue = app }} />
        </AppProvider>
      )

      expect(appValue).toBeDefined()
      expect(screen.getByTestId('app-name').textContent).toBe('Admin Dashboard')
      expect(screen.getByTestId('app-description').textContent).toBe('Manage your application')
      expect(screen.getByTestId('app-base-path').textContent).toBe('/admin')
    })

    it('should provide empty navigation by default', () => {
      const config: AppConfig = { name: 'Test App' }

      render(
        <AppProvider config={config}>
          <AppConsumer onApp={() => {}} />
        </AppProvider>
      )

      expect(screen.getByTestId('nav-count').textContent).toBe('0')
    })

    it('should provide navigation groups', () => {
      const config: AppConfig = { name: 'Test App' }
      const navigation: NavGroup[] = [
        {
          label: 'Main',
          items: [
            { title: 'Dashboard', url: '/dashboard', icon: Home },
            { title: 'Users', url: '/users', icon: Users },
          ],
        },
        {
          label: 'Settings',
          items: [
            { title: 'Settings', url: '/settings', icon: Settings },
          ],
        },
      ]

      let appValue: AppContextValue | undefined

      render(
        <AppProvider config={config} navigation={navigation}>
          <AppConsumer onApp={(app) => { appValue = app }} />
        </AppProvider>
      )

      expect(screen.getByTestId('nav-count').textContent).toBe('2')
      expect(appValue!.navigation[0].label).toBe('Main')
      expect(appValue!.navigation[0].items.length).toBe(2)
      expect(appValue!.navigation[1].label).toBe('Settings')
    })

    it('should provide user identity when authenticated', () => {
      const config: AppConfig = { name: 'Test App' }
      const user: UserIdentity = {
        id: 'user-123',
        fullName: 'John Doe',
        email: 'john@example.com',
        avatar: 'https://example.com/avatar.jpg',
      }

      let appValue: AppContextValue | undefined

      render(
        <AppProvider config={config} user={user}>
          <AppConsumer onApp={(app) => { appValue = app }} />
        </AppProvider>
      )

      expect(screen.getByTestId('user-name').textContent).toBe('John Doe')
      expect(appValue!.user?.id).toBe('user-123')
      expect(appValue!.user?.email).toBe('john@example.com')
      expect(appValue!.user?.avatar).toBe('https://example.com/avatar.jpg')
    })

    it('should indicate no user when not authenticated', () => {
      const config: AppConfig = { name: 'Test App' }

      render(
        <AppProvider config={config}>
          <AppConsumer onApp={() => {}} />
        </AppProvider>
      )

      expect(screen.getByTestId('user-name').textContent).toBe('none')
    })

    it('should provide loading state', () => {
      const config: AppConfig = { name: 'Test App' }

      render(
        <AppProvider config={config} isLoading={true}>
          <AppConsumer onApp={() => {}} />
        </AppProvider>
      )

      expect(screen.getByTestId('is-loading').textContent).toBe('true')
    })

    it('should indicate not loading by default', () => {
      const config: AppConfig = { name: 'Test App' }

      render(
        <AppProvider config={config}>
          <AppConsumer onApp={() => {}} />
        </AppProvider>
      )

      expect(screen.getByTestId('is-loading').textContent).toBe('undefined')
    })

    it('should handle config with logo', () => {
      const config: AppConfig = {
        name: 'Test App',
        logo: <img src="/logo.svg" alt="Logo" data-testid="app-logo" />,
      }

      let appValue: AppContextValue | undefined

      render(
        <AppProvider config={config}>
          <AppConsumer onApp={(app) => { appValue = app }} />
        </AppProvider>
      )

      expect(appValue!.config.logo).toBeDefined()
    })

    it('should support nested navigation items', () => {
      const config: AppConfig = { name: 'Test App' }
      const navigation: NavGroup[] = [
        {
          label: 'Main',
          items: [
            {
              title: 'Dashboard',
              url: '/dashboard',
              items: [
                { title: 'Overview', url: '/dashboard/overview' },
                { title: 'Analytics', url: '/dashboard/analytics' },
              ],
            },
          ],
        },
      ]

      let appValue: AppContextValue | undefined

      render(
        <AppProvider config={config} navigation={navigation}>
          <AppConsumer onApp={(app) => { appValue = app }} />
        </AppProvider>
      )

      expect(appValue!.navigation[0].items[0].items?.length).toBe(2)
      expect(appValue!.navigation[0].items[0].items?.[0].title).toBe('Overview')
    })

    it('should support active state on nav items', () => {
      const config: AppConfig = { name: 'Test App' }
      const navigation: NavGroup[] = [
        {
          label: 'Main',
          items: [
            { title: 'Dashboard', url: '/dashboard', isActive: true },
            { title: 'Users', url: '/users', isActive: false },
          ],
        },
      ]

      let appValue: AppContextValue | undefined

      render(
        <AppProvider config={config} navigation={navigation}>
          <AppConsumer onApp={(app) => { appValue = app }} />
        </AppProvider>
      )

      expect(appValue!.navigation[0].items[0].isActive).toBe(true)
      expect(appValue!.navigation[0].items[1].isActive).toBe(false)
    })
  })

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

      expect(() => {
        render(<AppConsumer onApp={() => {}} />)
      }).toThrow('useApp must be used within an AppProvider')

      consoleError.mockRestore()
    })
  })

  describe('user identity edge cases', () => {
    it('should handle user with minimal fields', () => {
      const config: AppConfig = { name: 'Test App' }
      const user: UserIdentity = { id: 'user-456' }

      let appValue: AppContextValue | undefined

      render(
        <AppProvider config={config} user={user}>
          <AppConsumer onApp={(app) => { appValue = app }} />
        </AppProvider>
      )

      expect(appValue!.user?.id).toBe('user-456')
      expect(appValue!.user?.fullName).toBeUndefined()
      expect(appValue!.user?.email).toBeUndefined()
    })

    it('should handle user with custom properties', () => {
      const config: AppConfig = { name: 'Test App' }
      const user: UserIdentity = {
        id: 'user-789',
        fullName: 'Jane Doe',
        role: 'admin',
        department: 'Engineering',
      }

      let appValue: AppContextValue | undefined

      render(
        <AppProvider config={config} user={user}>
          <AppConsumer onApp={(app) => { appValue = app }} />
        </AppProvider>
      )

      expect(appValue!.user?.role).toBe('admin')
      expect(appValue!.user?.department).toBe('Engineering')
    })
  })

  describe('config edge cases', () => {
    it('should handle config with only required fields', () => {
      const config: AppConfig = { name: 'Minimal App' }

      render(
        <AppProvider config={config}>
          <AppConsumer onApp={() => {}} />
        </AppProvider>
      )

      expect(screen.getByTestId('app-name').textContent).toBe('Minimal App')
      expect(screen.getByTestId('app-description').textContent).toBe('none')
      expect(screen.getByTestId('app-base-path').textContent).toBe('none')
    })

    it('should handle config with empty base path', () => {
      const config: AppConfig = { name: 'Test App', basePath: '' }

      let appValue: AppContextValue | undefined

      render(
        <AppProvider config={config}>
          <AppConsumer onApp={(app) => { appValue = app }} />
        </AppProvider>
      )

      expect(appValue!.config.basePath).toBe('')
    })
  })
})
