import type { AccountSettingTab } from '../constants'
import type { AppContextValue } from '@/context/app-context'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { fireEvent, render, screen } from '@testing-library/react'
import { useState } from 'react'
import { useAppContext } from '@/context/app-context'
import { baseProviderContextValue, useProviderContext } from '@/context/provider-context'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
import { ACCOUNT_SETTING_TAB } from '../constants'
import AccountSetting from '../index'

const mockResetModelProviderListExpanded = vi.fn()

vi.mock('@/context/provider-context', async (importOriginal) => {
  const actual = await importOriginal<typeof import('@/context/provider-context')>()
  return {
    ...actual,
    useProviderContext: vi.fn(),
  }
})

vi.mock('@/context/app-context', async (importOriginal) => {
  const actual = await importOriginal<typeof import('@/context/app-context')>()
  return {
    ...actual,
    useAppContext: vi.fn(),
  }
})

vi.mock('@/next/navigation', () => ({
  useRouter: vi.fn(() => ({
    push: vi.fn(),
    replace: vi.fn(),
    prefetch: vi.fn(),
  })),
  usePathname: vi.fn(() => '/'),
  useParams: vi.fn(() => ({})),
  useSearchParams: vi.fn(() => ({ get: vi.fn() })),
}))

vi.mock('@/hooks/use-breakpoints', () => ({
  MediaType: {
    mobile: 'mobile',
    tablet: 'tablet',
    pc: 'pc',
  },
  default: vi.fn(),
}))

vi.mock('@/app/components/header/account-setting/model-provider-page/hooks', () => ({
  useDefaultModel: vi.fn(() => ({ data: null, isLoading: false })),
  useUpdateDefaultModel: vi.fn(() => ({ trigger: vi.fn() })),
  useUpdateModelList: vi.fn(() => vi.fn()),
  useInvalidateDefaultModel: vi.fn(() => vi.fn()),
  useModelList: vi.fn(() => ({ data: [], isLoading: false })),
  useSystemDefaultModelAndModelList: vi.fn(() => [null, vi.fn()]),
}))

vi.mock('@/app/components/header/account-setting/model-provider-page/atoms', () => ({
  useResetModelProviderListExpanded: () => mockResetModelProviderListExpanded,
}))

vi.mock('@/service/use-datasource', () => ({
  useGetDataSourceListAuth: vi.fn(() => ({ data: { result: [] } })),
}))

vi.mock('@/service/use-common', () => ({
  useApiBasedExtensions: vi.fn(() => ({ data: [], isPending: false })),
  useMembers: vi.fn(() => ({ data: { accounts: [] }, refetch: vi.fn() })),
  useProviderContext: vi.fn(),
}))

const baseAppContextValue: AppContextValue = {
  userProfile: {
    id: '1',
    name: 'Test User',
    email: 'test@example.com',
    avatar: '',
    avatar_url: '',
    is_password_set: false,
  },
  mutateUserProfile: vi.fn(),
  currentWorkspace: {
    id: '1',
    name: 'Workspace',
    plan: '',
    status: '',
    created_at: 0,
    role: 'owner',
    providers: [],
    trial_credits: 0,
    trial_credits_used: 0,
    next_credit_reset_date: 0,
  },
  isCurrentWorkspaceManager: true,
  isCurrentWorkspaceOwner: true,
  isCurrentWorkspaceEditor: true,
  isCurrentWorkspaceDatasetOperator: false,
  mutateCurrentWorkspace: vi.fn(),
  langGeniusVersionInfo: {
    current_env: 'testing',
    current_version: '0.1.0',
    latest_version: '0.1.0',
    release_date: '',
    release_notes: '',
    version: '0.1.0',
    can_auto_update: false,
  },
  useSelector: vi.fn(),
  isLoadingCurrentWorkspace: false,
  isValidatingCurrentWorkspace: false,
}

describe('AccountSetting', () => {
  const mockOnCancel = vi.fn()
  const mockOnTabChange = vi.fn()
  const renderAccountSetting = (props?: {
    initialTab?: AccountSettingTab
    onCancel?: () => void
    onTabChange?: (tab: AccountSettingTab) => void
  }) => {
    const {
      initialTab = ACCOUNT_SETTING_TAB.MEMBERS,
      onCancel = mockOnCancel,
      onTabChange = mockOnTabChange,
    } = props ?? {}

    const StatefulAccountSetting = () => {
      const [activeTab, setActiveTab] = useState<AccountSettingTab>(initialTab)

      return (
        <AccountSetting
          onCancelAction={onCancel}
          activeTab={activeTab}
          onTabChangeAction={(tab) => {
            setActiveTab(tab)
            onTabChange(tab)
          }}
        />
      )
    }

    return render(
      <QueryClientProvider client={new QueryClient()}>
        <StatefulAccountSetting />
      </QueryClientProvider>,
    )
  }

  beforeEach(() => {
    vi.clearAllMocks()
    vi.mocked(useProviderContext).mockReturnValue({
      ...baseProviderContextValue,
      enableBilling: true,
      enableReplaceWebAppLogo: true,
    })
    vi.mocked(useAppContext).mockReturnValue(baseAppContextValue)
    vi.mocked(useBreakpoints).mockReturnValue(MediaType.pc)
  })

  describe('Rendering', () => {
    it('should render the sidebar with correct menu items', () => {
      // Act
      renderAccountSetting()

      // Assert
      expect(screen.getByText('common.userProfile.settings')).toBeInTheDocument()
      expect(screen.getByText('common.settings.provider')).toBeInTheDocument()
      expect(screen.getAllByText('common.settings.members').length).toBeGreaterThan(0)
      expect(screen.getByText('common.settings.billing')).toBeInTheDocument()
      expect(screen.getByText('common.settings.dataSource')).toBeInTheDocument()
      expect(screen.getByText('common.settings.apiBasedExtension')).toBeInTheDocument()
      expect(screen.getByText('custom.custom')).toBeInTheDocument()
      expect(screen.getAllByText('common.settings.language').length).toBeGreaterThan(0)
    })

    it('should respect the initial tab', () => {
      // Act
      renderAccountSetting({ initialTab: ACCOUNT_SETTING_TAB.DATA_SOURCE })

      // Assert
      // Check that the active item title is Data Source
      const titles = screen.getAllByText('common.settings.dataSource')
      // One in sidebar, one in header.
      expect(titles.length).toBeGreaterThan(1)
    })

    it('should hide sidebar labels on mobile', () => {
      // Arrange
      vi.mocked(useBreakpoints).mockReturnValue(MediaType.mobile)

      // Act
      renderAccountSetting()

      // Assert
      // On mobile, the labels should not be rendered as per the implementation
      expect(screen.queryByText('common.settings.provider')).not.toBeInTheDocument()
    })

    it('should filter items for dataset operator', () => {
      // Arrange
      vi.mocked(useAppContext).mockReturnValue({
        ...baseAppContextValue,
        isCurrentWorkspaceDatasetOperator: true,
      })

      // Act
      renderAccountSetting()

      // Assert
      expect(screen.queryByText('common.settings.provider')).not.toBeInTheDocument()
      expect(screen.queryByText('common.settings.members')).not.toBeInTheDocument()
      expect(screen.getByText('common.settings.language')).toBeInTheDocument()
    })

    it('should hide billing and custom tabs when disabled', () => {
      // Arrange
      vi.mocked(useProviderContext).mockReturnValue({
        ...baseProviderContextValue,
        enableBilling: false,
        enableReplaceWebAppLogo: false,
      })

      // Act
      renderAccountSetting()

      // Assert
      expect(screen.queryByText('common.settings.billing')).not.toBeInTheDocument()
      expect(screen.queryByText('custom.custom')).not.toBeInTheDocument()
    })
  })

  describe('Tab Navigation', () => {
    it('should change active tab when clicking on menu item', () => {
      // Arrange
      renderAccountSetting({ onTabChange: mockOnTabChange })

      // Act
      fireEvent.click(screen.getByText('common.settings.provider'))

      // Assert
      expect(mockOnTabChange).toHaveBeenCalledWith(ACCOUNT_SETTING_TAB.PROVIDER)
      // Check for content from ModelProviderPage
      expect(screen.getByText('common.modelProvider.models')).toBeInTheDocument()
    })

    it('should navigate through various tabs and show correct details', () => {
      // Act & Assert
      renderAccountSetting()

      // Billing
      fireEvent.click(screen.getByText('common.settings.billing'))
      // Billing Page renders plansCommon.plan if data is loaded, or generic text.
      // Checking for title in header which is always there
      expect(screen.getAllByText('common.settings.billing').length).toBeGreaterThan(1)

      // Data Source
      fireEvent.click(screen.getByText('common.settings.dataSource'))
      expect(screen.getAllByText('common.settings.dataSource').length).toBeGreaterThan(1)

      // API Based Extension
      fireEvent.click(screen.getByText('common.settings.apiBasedExtension'))
      expect(screen.getAllByText('common.settings.apiBasedExtension').length).toBeGreaterThan(1)

      // Custom
      fireEvent.click(screen.getByText('custom.custom'))
      // Custom Page uses 'custom.custom' key as well.
      expect(screen.getAllByText('custom.custom').length).toBeGreaterThan(1)

      // Language
      fireEvent.click(screen.getAllByText('common.settings.language')[0])
      expect(screen.getAllByText('common.settings.language').length).toBeGreaterThan(1)

      // Members
      fireEvent.click(screen.getAllByText('common.settings.members')[0])
      expect(screen.getAllByText('common.settings.members').length).toBeGreaterThan(1)
    })
  })

  describe('Interactions', () => {
    it('should call onCancel when clicking close button', () => {
      // Act
      renderAccountSetting()
      const closeIcon = document.querySelector('.i-ri-close-line')
      const closeButton = closeIcon?.closest('button')
      expect(closeButton).not.toBeNull()
      fireEvent.click(closeButton!)

      // Assert
      expect(mockOnCancel).toHaveBeenCalled()
    })

    it('should call onCancel when pressing Escape key', () => {
      // Act
      renderAccountSetting()
      fireEvent.keyDown(document, { key: 'Escape' })

      // Assert
      expect(mockOnCancel).toHaveBeenCalled()
    })

    it('should update search value in provider tab', () => {
      // Arrange
      renderAccountSetting({ initialTab: ACCOUNT_SETTING_TAB.PROVIDER })

      // Act
      const input = screen.getByRole('textbox')
      fireEvent.change(input, { target: { value: 'test-search' } })

      // Assert
      expect(input).toHaveValue('test-search')
      expect(screen.getByText('common.modelProvider.models')).toBeInTheDocument()
    })

    it('should handle scroll event in panel', () => {
      // Act
      renderAccountSetting()
      const scrollContainer = screen.getByRole('dialog').querySelector('.overscroll-contain')

      // Assert
      expect(scrollContainer).toBeInTheDocument()
      if (scrollContainer) {
        // Scroll down
        fireEvent.scroll(scrollContainer, { target: { scrollTop: 100 } })
        expect(scrollContainer).toHaveClass('overscroll-contain')

        // Scroll back up
        fireEvent.scroll(scrollContainer, { target: { scrollTop: 0 } })
      }
    })
  })
})
