import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
import { render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import EmployeeStatusCard from '../EmployeeStatusCard'
import { onboardingService } from '../../services/onboarding.service'
import type { EmployeeStatusResponse } from '../../types/onboarding'

// Mock the onboarding service
vi.mock('../../services/onboarding.service', () => ({
  onboardingService: {
    getEmployeeStatus: vi.fn(),
    offboardEmployee: vi.fn(),
  },
}))

// Mock Next.js navigation
vi.mock('next/navigation', () => ({
  useRouter: () => ({
    push: vi.fn(),
    refresh: vi.fn(),
  }),
}))

describe('EmployeeStatusCard', () => {
  // Mock window.confirm
  const originalConfirm = window.confirm

  beforeEach(() => {
    // Mock window.confirm to return true by default
    window.confirm = vi.fn(() => true)
  })

  afterEach(() => {
    // Restore original confirm
    window.confirm = originalConfirm
  })

  const mockEmployeeStatus: EmployeeStatusResponse = {
    employee: {
      tenant_id: 1,
      seq_no: 1,
      tenant_emp_code: 'PWD0001',
      name: '王明',
      keycloak_user_id: '550e8400-e29b-41d4-a716-446655440000',
      keycloak_username: 'wang.ming',
      hire_date: '2026-02-21',
      resign_date: null,
      employment_status: 'active',
      storage_quota_gb: 20,
      email_quota_mb: 5120,
    },
    departments: [
      {
        department_id: 9,
        department_name: '玄鐵風能',
        position: '資深工程師',
        membership_type: 'permanent',
        joined_at: '2026-02-21T10:00:00',
      },
      {
        department_id: 10,
        department_name: '國際碳權',
        position: '顧問',
        membership_type: 'concurrent',
        joined_at: '2026-02-21T10:00:00',
      },
    ],
    roles: [
      {
        role_id: 1,
        role_name: 'HR管理員',
        role_code: 'HR_ADMIN',
        assigned_at: '2026-02-21T10:00:00',
      },
      {
        role_id: 2,
        role_name: '系統管理員',
        role_code: 'SYS_ADMIN',
        assigned_at: '2026-02-21T10:00:00',
      },
    ],
    services: [
      {
        service_id: 1,
        service_name: '單一簽入',
        service_code: 'SSO',
        quota_gb: null,
        quota_mb: null,
        enabled_at: '2026-02-21T10:00:00',
      },
      {
        service_id: 2,
        service_name: '雲端硬碟',
        service_code: 'DRIVE',
        quota_gb: 20,
        quota_mb: null,
        enabled_at: '2026-02-21T10:00:00',
      },
      {
        service_id: 3,
        service_name: '電子郵件',
        service_code: 'EMAIL',
        quota_gb: null,
        quota_mb: 5120,
        enabled_at: '2026-02-21T10:00:00',
      },
    ],
  }

  beforeEach(() => {
    vi.clearAllMocks()
  })

  describe('Loading State', () => {
    it('should show loading spinner while fetching data', () => {
      vi.mocked(onboardingService.getEmployeeStatus).mockImplementation(
        () => new Promise(() => {}) // Never resolves
      )

      render(<EmployeeStatusCard tenantId={1} seqNo={1} />)

      expect(screen.getByText(/Loading/i)).toBeInTheDocument()
    })
  })

  describe('Employee Basic Information', () => {
    it('should display employee basic information', async () => {
      vi.mocked(onboardingService.getEmployeeStatus).mockResolvedValueOnce(mockEmployeeStatus)

      render(<EmployeeStatusCard tenantId={1} seqNo={1} />)

      await waitFor(() => {
        expect(screen.getByText('王明')).toBeInTheDocument()
        expect(screen.getByText('PWD0001')).toBeInTheDocument()
        expect(screen.getByText('wang.ming')).toBeInTheDocument()
        expect(screen.getByText(/2026-02-21/)).toBeInTheDocument()
      })
    })

    it('should display employment status badge', async () => {
      vi.mocked(onboardingService.getEmployeeStatus).mockResolvedValueOnce(mockEmployeeStatus)

      render(<EmployeeStatusCard tenantId={1} seqNo={1} />)

      await waitFor(() => {
        expect(screen.getByText(/Active/i)).toBeInTheDocument()
      })
    })

    it('should display storage and email quotas', async () => {
      vi.mocked(onboardingService.getEmployeeStatus).mockResolvedValueOnce(mockEmployeeStatus)

      render(<EmployeeStatusCard tenantId={1} seqNo={1} />)

      await waitFor(() => {
        const storageQuotas = screen.getAllByText(/20 GB/i)
        expect(storageQuotas.length).toBeGreaterThan(0)

        const emailQuotas = screen.getAllByText(/5120 MB/i)
        expect(emailQuotas.length).toBeGreaterThan(0)
      })
    })

    it('should display Keycloak information', async () => {
      vi.mocked(onboardingService.getEmployeeStatus).mockResolvedValueOnce(mockEmployeeStatus)

      render(<EmployeeStatusCard tenantId={1} seqNo={1} />)

      await waitFor(() => {
        expect(screen.getByText(/550e8400-e29b-41d4-a716-446655440000/i)).toBeInTheDocument()
      })
    })
  })

  describe('Department List', () => {
    it('should display all departments', async () => {
      vi.mocked(onboardingService.getEmployeeStatus).mockResolvedValueOnce(mockEmployeeStatus)

      render(<EmployeeStatusCard tenantId={1} seqNo={1} />)

      await waitFor(() => {
        expect(screen.getByText('玄鐵風能')).toBeInTheDocument()
        expect(screen.getByText('國際碳權')).toBeInTheDocument()
      })
    })

    it('should display department positions', async () => {
      vi.mocked(onboardingService.getEmployeeStatus).mockResolvedValueOnce(mockEmployeeStatus)

      render(<EmployeeStatusCard tenantId={1} seqNo={1} />)

      await waitFor(() => {
        expect(screen.getByText('資深工程師')).toBeInTheDocument()
        expect(screen.getByText('顧問')).toBeInTheDocument()
      })
    })

    it('should display membership types', async () => {
      vi.mocked(onboardingService.getEmployeeStatus).mockResolvedValueOnce(mockEmployeeStatus)

      render(<EmployeeStatusCard tenantId={1} seqNo={1} />)

      await waitFor(() => {
        expect(screen.getByText(/Permanent/i)).toBeInTheDocument()
        expect(screen.getByText(/Concurrent/i)).toBeInTheDocument()
      })
    })

    it('should show message when no departments assigned', async () => {
      const statusWithNoDepts: EmployeeStatusResponse = {
        ...mockEmployeeStatus,
        departments: [],
      }

      vi.mocked(onboardingService.getEmployeeStatus).mockResolvedValueOnce(statusWithNoDepts)

      render(<EmployeeStatusCard tenantId={1} seqNo={1} />)

      await waitFor(() => {
        expect(screen.getByText(/No departments assigned/i)).toBeInTheDocument()
      })
    })
  })

  describe('Role List', () => {
    it('should display all roles', async () => {
      vi.mocked(onboardingService.getEmployeeStatus).mockResolvedValueOnce(mockEmployeeStatus)

      render(<EmployeeStatusCard tenantId={1} seqNo={1} />)

      await waitFor(() => {
        expect(screen.getByText('HR管理員')).toBeInTheDocument()
        expect(screen.getByText('系統管理員')).toBeInTheDocument()
      })
    })

    it('should display role codes', async () => {
      vi.mocked(onboardingService.getEmployeeStatus).mockResolvedValueOnce(mockEmployeeStatus)

      render(<EmployeeStatusCard tenantId={1} seqNo={1} />)

      await waitFor(() => {
        expect(screen.getByText('HR_ADMIN')).toBeInTheDocument()
        expect(screen.getByText('SYS_ADMIN')).toBeInTheDocument()
      })
    })

    it('should show message when no roles assigned', async () => {
      const statusWithNoRoles: EmployeeStatusResponse = {
        ...mockEmployeeStatus,
        roles: [],
      }

      vi.mocked(onboardingService.getEmployeeStatus).mockResolvedValueOnce(statusWithNoRoles)

      render(<EmployeeStatusCard tenantId={1} seqNo={1} />)

      await waitFor(() => {
        expect(screen.getByText(/No roles assigned/i)).toBeInTheDocument()
      })
    })
  })

  describe('Service List', () => {
    it('should display all enabled services', async () => {
      vi.mocked(onboardingService.getEmployeeStatus).mockResolvedValueOnce(mockEmployeeStatus)

      render(<EmployeeStatusCard tenantId={1} seqNo={1} />)

      await waitFor(() => {
        expect(screen.getByText('單一簽入')).toBeInTheDocument()
        expect(screen.getByText('雲端硬碟')).toBeInTheDocument()
        expect(screen.getByText('電子郵件')).toBeInTheDocument()
      })
    })

    it('should display service codes', async () => {
      vi.mocked(onboardingService.getEmployeeStatus).mockResolvedValueOnce(mockEmployeeStatus)

      render(<EmployeeStatusCard tenantId={1} seqNo={1} />)

      await waitFor(() => {
        expect(screen.getByText('SSO')).toBeInTheDocument()
        expect(screen.getByText('DRIVE')).toBeInTheDocument()
        expect(screen.getByText('EMAIL')).toBeInTheDocument()
      })
    })

    it('should display service quotas when available', async () => {
      vi.mocked(onboardingService.getEmployeeStatus).mockResolvedValueOnce(mockEmployeeStatus)

      render(<EmployeeStatusCard tenantId={1} seqNo={1} />)

      await waitFor(() => {
        // Drive quota
        const driveQuota = screen.getAllByText(/20 GB/i)
        expect(driveQuota.length).toBeGreaterThan(0)

        // Email quota
        const emailQuota = screen.getAllByText(/5120 MB/i)
        expect(emailQuota.length).toBeGreaterThan(0)
      })
    })

    it('should show message when no services enabled', async () => {
      const statusWithNoServices: EmployeeStatusResponse = {
        ...mockEmployeeStatus,
        services: [],
      }

      vi.mocked(onboardingService.getEmployeeStatus).mockResolvedValueOnce(statusWithNoServices)

      render(<EmployeeStatusCard tenantId={1} seqNo={1} />)

      await waitFor(() => {
        expect(screen.getByText(/No services enabled/i)).toBeInTheDocument()
      })
    })
  })

  describe('Error Handling', () => {
    it('should display error message when API fails', async () => {
      const errorMessage = 'Employee not found'
      vi.mocked(onboardingService.getEmployeeStatus).mockRejectedValueOnce({
        response: {
          data: { detail: errorMessage },
          status: 404,
        },
      })

      render(<EmployeeStatusCard tenantId={1} seqNo={999} />)

      await waitFor(() => {
        expect(screen.getByText(errorMessage)).toBeInTheDocument()
      })
    })

    it('should display generic error message for unknown errors', async () => {
      vi.mocked(onboardingService.getEmployeeStatus).mockRejectedValueOnce(new Error('Network error'))

      render(<EmployeeStatusCard tenantId={1} seqNo={1} />)

      await waitFor(() => {
        expect(screen.getByText(/Failed to load employee status/i)).toBeInTheDocument()
      })
    })
  })

  describe('Offboard Action', () => {
    it('should display offboard button for active employees', async () => {
      vi.mocked(onboardingService.getEmployeeStatus).mockResolvedValueOnce(mockEmployeeStatus)

      render(<EmployeeStatusCard tenantId={1} seqNo={1} showActions={true} />)

      await waitFor(() => {
        expect(screen.getByRole('button', { name: /Offboard/i })).toBeInTheDocument()
      })
    })

    it('should not display offboard button when showActions is false', async () => {
      vi.mocked(onboardingService.getEmployeeStatus).mockResolvedValueOnce(mockEmployeeStatus)

      render(<EmployeeStatusCard tenantId={1} seqNo={1} showActions={false} />)

      await waitFor(() => {
        expect(screen.queryByRole('button', { name: /Offboard/i })).not.toBeInTheDocument()
      })
    })

    it('should call offboardEmployee when offboard button is clicked', async () => {
      const user = userEvent.setup()
      vi.mocked(onboardingService.getEmployeeStatus).mockResolvedValueOnce(mockEmployeeStatus)
      vi.mocked(onboardingService.offboardEmployee).mockResolvedValueOnce({
        message: 'Employee offboarded successfully',
        employee: { tenant_emp_code: 'PWD0001', resign_date: '2026-02-21' },
        summary: { departments_removed: 2, roles_revoked: 2, services_disabled: 3 },
      })

      render(<EmployeeStatusCard tenantId={1} seqNo={1} showActions={true} />)

      await waitFor(() => {
        expect(screen.getByRole('button', { name: /Offboard/i })).toBeInTheDocument()
      })

      const offboardButton = screen.getByRole('button', { name: /Offboard/i })
      await user.click(offboardButton)

      await waitFor(() => {
        expect(onboardingService.offboardEmployee).toHaveBeenCalledWith(1, 1)
      })
    })

    it('should show success message after offboarding', async () => {
      const user = userEvent.setup()
      const offboardedStatus = {
        ...mockEmployeeStatus,
        employee: { ...mockEmployeeStatus.employee, employment_status: 'resigned' as const, resign_date: '2026-02-21' },
      }

      vi.mocked(onboardingService.getEmployeeStatus)
        .mockResolvedValueOnce(mockEmployeeStatus)  // Initial load
        .mockResolvedValueOnce(offboardedStatus)    // After offboard refresh

      vi.mocked(onboardingService.offboardEmployee).mockResolvedValueOnce({
        message: 'Employee offboarded successfully',
        employee: { tenant_emp_code: 'PWD0001', resign_date: '2026-02-21' },
        summary: { departments_removed: 2, roles_revoked: 2, services_disabled: 3 },
      })

      render(<EmployeeStatusCard tenantId={1} seqNo={1} showActions={true} />)

      await waitFor(() => {
        expect(screen.getByRole('button', { name: /Offboard/i })).toBeInTheDocument()
      })

      const offboardButton = screen.getByRole('button', { name: /Offboard/i })
      await user.click(offboardButton)

      await waitFor(() => {
        expect(screen.getByText(/Employee offboarded successfully/i)).toBeInTheDocument()
      })
    })

    it('should handle offboard errors gracefully', async () => {
      const user = userEvent.setup()
      const errorMessage = 'Failed to offboard employee'

      vi.mocked(onboardingService.getEmployeeStatus).mockResolvedValueOnce(mockEmployeeStatus)
      vi.mocked(onboardingService.offboardEmployee).mockRejectedValueOnce({
        response: {
          data: { detail: errorMessage },
          status: 500,
        },
      })

      render(<EmployeeStatusCard tenantId={1} seqNo={1} showActions={true} />)

      await waitFor(() => {
        expect(screen.getByRole('button', { name: /Offboard/i })).toBeInTheDocument()
      })

      const offboardButton = screen.getByRole('button', { name: /Offboard/i })
      await user.click(offboardButton)

      await waitFor(() => {
        expect(screen.getByText(errorMessage)).toBeInTheDocument()
      })
    })
  })

  describe('Refresh Functionality', () => {
    it('should refresh employee status when refresh is called', async () => {
      vi.mocked(onboardingService.getEmployeeStatus).mockResolvedValue(mockEmployeeStatus)

      const { rerender } = render(<EmployeeStatusCard tenantId={1} seqNo={1} />)

      await waitFor(() => {
        expect(screen.getByText('王明')).toBeInTheDocument()
      })

      expect(onboardingService.getEmployeeStatus).toHaveBeenCalledTimes(1)

      // Trigger refresh by rerendering with key change
      rerender(<EmployeeStatusCard tenantId={1} seqNo={1} key="refresh" />)

      await waitFor(() => {
        expect(onboardingService.getEmployeeStatus).toHaveBeenCalledTimes(2)
      })
    })
  })
})
