import type {
  PluginDeclaration,
  UpdateFromGitHubPayload,
  UpdateFromMarketPlacePayload,
  UpdatePluginModalType,
} from '../../types'
import { toast } from '@langgenius/dify-ui/toast'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
import * as React from 'react'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { PluginCategoryEnum, PluginSource, TaskStatus } from '../../types'
import DowngradeWarningModal from '../downgrade-warning'
import FromGitHub from '../from-github'
import UpdateFromMarketplace from '../from-market-place'
import UpdatePlugin from '../index'
import PluginVersionPicker from '../plugin-version-picker'

// Mock useGetLanguage context
vi.mock('@/context/i18n', () => ({
  useGetLanguage: () => 'en-US',
}))

// Mock app context for useGetIcon
vi.mock('@/context/app-context', () => ({
  useSelector: () => ({ id: 'test-workspace-id' }),
}))

// Mock hooks/use-timestamp
vi.mock('@/hooks/use-timestamp', () => ({
  default: () => ({
    formatDate: (timestamp: number, _format: string) => {
      const date = new Date(timestamp * 1000)
      return date.toISOString().split('T')[0]
    },
  }),
}))

// Mock plugins service
const mockUpdateFromMarketPlace = vi.fn()
vi.mock('@/service/plugins', () => ({
  updateFromMarketPlace: (params: unknown) => mockUpdateFromMarketPlace(params),
  checkTaskStatus: vi.fn().mockResolvedValue({
    task: {
      plugins: [{ plugin_unique_identifier: 'test-target-id', status: 'success' }],
    },
  }),
}))

// Mock use-plugins hooks
const mockHandleInstallTaskStart = vi.fn()
const mockMutateAsync = vi.fn()

vi.mock('@/service/use-plugins', () => ({
  usePluginTaskList: () => ({
    handleInstallTaskStart: mockHandleInstallTaskStart,
  }),
  useRemoveAutoUpgrade: () => ({
    mutateAsync: mockMutateAsync,
  }),
  useVersionListOfPlugin: () => ({
    data: {
      data: {
        versions: [
          { version: '1.0.0', unique_identifier: 'plugin-v1.0.0', created_at: 1700000000 },
          { version: '1.1.0', unique_identifier: 'plugin-v1.1.0', created_at: 1700100000 },
          { version: '2.0.0', unique_identifier: 'plugin-v2.0.0', created_at: 1700200000 },
        ],
      },
    },
  }),
}))

// Mock checkTaskStatus
const mockCheck = vi.fn()
const mockStop = vi.fn()
vi.mock('../../install-plugin/base/check-task-status', () => ({
  default: () => ({
    check: mockCheck,
    stop: mockStop,
  }),
}))

const toastErrorSpy = vi.spyOn(toast, 'error').mockReturnValue('toast-error')

// Mock InstallFromGitHub component
vi.mock('../../install-plugin/install-from-github', () => ({
  default: ({ updatePayload, onClose, onSuccess }: {
    updatePayload: UpdateFromGitHubPayload
    onClose: () => void
    onSuccess: () => void
  }) => (
    <div data-testid="install-from-github">
      <span data-testid="github-payload">{JSON.stringify(updatePayload)}</span>
      <button data-testid="github-close" onClick={onClose}>Close</button>
      <button data-testid="github-success" onClick={onSuccess}>Success</button>
    </div>
  ),
}))

// ================================
// Test Data Factories
// ================================

const createMockPluginDeclaration = (overrides: Partial<PluginDeclaration> = {}): PluginDeclaration => ({
  plugin_unique_identifier: 'test-plugin-id',
  version: '1.0.0',
  author: 'test-author',
  icon: 'test-icon.png',
  name: 'Test Plugin',
  category: PluginCategoryEnum.tool,
  label: { 'en-US': 'Test Plugin' } as PluginDeclaration['label'],
  description: { 'en-US': 'A test plugin' } as PluginDeclaration['description'],
  created_at: '2024-01-01',
  resource: {},
  plugins: {},
  verified: true,
  endpoint: { settings: [], endpoints: [] },
  model: {},
  tags: [],
  agent_strategy: {},
  meta: { version: '1.0.0' },
  trigger: {
    events: [],
    identity: {
      author: 'test',
      name: 'test',
      label: { 'en-US': 'Test' } as PluginDeclaration['label'],
      description: { 'en-US': 'Test' } as PluginDeclaration['description'],
      icon: 'test.png',
      tags: [],
    },
    subscription_constructor: {
      credentials_schema: [],
      oauth_schema: { client_schema: [], credentials_schema: [] },
      parameters: [],
    },
    subscription_schema: [],
  },
  ...overrides,
})

const createMockMarketPlacePayload = (overrides: Partial<UpdateFromMarketPlacePayload> = {}): UpdateFromMarketPlacePayload => ({
  category: PluginCategoryEnum.tool,
  originalPackageInfo: {
    id: 'original-id',
    payload: createMockPluginDeclaration(),
  },
  targetPackageInfo: {
    id: 'test-target-id',
    version: '2.0.0',
  },
  ...overrides,
})

const createMockGitHubPayload = (overrides: Partial<UpdateFromGitHubPayload> = {}): UpdateFromGitHubPayload => ({
  originalPackageInfo: {
    id: 'github-original-id',
    repo: 'owner/repo',
    version: '1.0.0',
    package: 'test-package.difypkg',
    releases: [
      { tag_name: 'v1.0.0', assets: [{ id: 1, name: 'plugin.difypkg', browser_download_url: 'https://github.com/test' }] },
      { tag_name: 'v2.0.0', assets: [{ id: 2, name: 'plugin.difypkg', browser_download_url: 'https://github.com/test' }] },
    ],
  },
  ...overrides,
})

// Version list is provided by the mocked useVersionListOfPlugin hook

// ================================
// Helper Functions
// ================================

const createQueryClient = () => new QueryClient({
  defaultOptions: {
    queries: {
      retry: false,
    },
  },
})

const renderWithQueryClient = (ui: React.ReactElement) => {
  const queryClient = createQueryClient()
  return render(
    <QueryClientProvider client={queryClient}>
      {ui}
    </QueryClientProvider>,
  )
}

// ================================
// Test Suites
// ================================

describe('update-plugin', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    toastErrorSpy.mockClear()
    mockCheck.mockResolvedValue({ status: TaskStatus.success })
  })

  // ============================================================
  // UpdatePlugin (index.tsx) - Main Entry Component Tests
  // ============================================================
  describe('UpdatePlugin (index.tsx)', () => {
    describe('Rendering', () => {
      it('should render UpdateFromGitHub when type is github', () => {
        // Arrange
        const props: UpdatePluginModalType = {
          type: PluginSource.github,
          category: PluginCategoryEnum.tool,
          github: createMockGitHubPayload(),
          onCancel: vi.fn(),
          onSave: vi.fn(),
        }

        // Act
        render(<UpdatePlugin {...props} />)

        // Assert
        expect(screen.getByTestId('install-from-github')).toBeInTheDocument()
      })

      it('should render UpdateFromMarketplace when type is marketplace', () => {
        // Arrange
        const props: UpdatePluginModalType = {
          type: PluginSource.marketplace,
          category: PluginCategoryEnum.tool,
          marketPlace: createMockMarketPlacePayload(),
          onCancel: vi.fn(),
          onSave: vi.fn(),
        }

        // Act
        renderWithQueryClient(<UpdatePlugin {...props} />)

        // Assert
        expect(screen.getByText('plugin.upgrade.title')).toBeInTheDocument()
      })

      it('should render UpdateFromMarketplace for other plugin sources', () => {
        // Arrange
        const props: UpdatePluginModalType = {
          type: PluginSource.local,
          category: PluginCategoryEnum.tool,
          marketPlace: createMockMarketPlacePayload(),
          onCancel: vi.fn(),
          onSave: vi.fn(),
        }

        // Act
        renderWithQueryClient(<UpdatePlugin {...props} />)

        // Assert
        expect(screen.getByText('plugin.upgrade.title')).toBeInTheDocument()
      })
    })

    describe('Component Memoization', () => {
      it('should be memoized with React.memo', () => {
        // Verify the component is wrapped with React.memo
        expect(UpdatePlugin).toBeDefined()
        // The component should have $$typeof indicating it's a memo component
        expect((UpdatePlugin as { $$typeof?: symbol }).$$typeof?.toString()).toContain('Symbol')
      })
    })

    describe('Props Passing', () => {
      it('should pass correct props to UpdateFromGitHub', () => {
        // Arrange
        const githubPayload = createMockGitHubPayload()
        const onCancel = vi.fn()
        const onSave = vi.fn()
        const props: UpdatePluginModalType = {
          type: PluginSource.github,
          category: PluginCategoryEnum.tool,
          github: githubPayload,
          onCancel,
          onSave,
        }

        // Act
        render(<UpdatePlugin {...props} />)

        // Assert
        const payloadElement = screen.getByTestId('github-payload')
        expect(payloadElement.textContent).toBe(JSON.stringify(githubPayload))
      })

      it('should call onCancel when github close is triggered', () => {
        // Arrange
        const onCancel = vi.fn()
        const props: UpdatePluginModalType = {
          type: PluginSource.github,
          category: PluginCategoryEnum.tool,
          github: createMockGitHubPayload(),
          onCancel,
          onSave: vi.fn(),
        }

        // Act
        render(<UpdatePlugin {...props} />)
        fireEvent.click(screen.getByTestId('github-close'))

        // Assert
        expect(onCancel).toHaveBeenCalledTimes(1)
      })

      it('should call onSave when github success is triggered', () => {
        // Arrange
        const onSave = vi.fn()
        const props: UpdatePluginModalType = {
          type: PluginSource.github,
          category: PluginCategoryEnum.tool,
          github: createMockGitHubPayload(),
          onCancel: vi.fn(),
          onSave,
        }

        // Act
        render(<UpdatePlugin {...props} />)
        fireEvent.click(screen.getByTestId('github-success'))

        // Assert
        expect(onSave).toHaveBeenCalledTimes(1)
      })
    })
  })

  // ============================================================
  // FromGitHub (from-github.tsx) Tests
  // ============================================================
  describe('FromGitHub (from-github.tsx)', () => {
    describe('Rendering', () => {
      it('should render InstallFromGitHub with correct props', () => {
        // Arrange
        const payload = createMockGitHubPayload()
        const onSave = vi.fn()
        const onCancel = vi.fn()

        // Act
        render(
          <FromGitHub
            payload={payload}
            onSave={onSave}
            onCancel={onCancel}
          />,
        )

        // Assert
        expect(screen.getByTestId('install-from-github')).toBeInTheDocument()
      })
    })

    describe('Component Memoization', () => {
      it('should be memoized with React.memo', () => {
        expect(FromGitHub).toBeDefined()
        expect((FromGitHub as { $$typeof?: symbol }).$$typeof?.toString()).toContain('Symbol')
      })
    })

    describe('Event Handlers', () => {
      it('should call onCancel when onClose is triggered', () => {
        // Arrange
        const onCancel = vi.fn()

        // Act
        render(
          <FromGitHub
            payload={createMockGitHubPayload()}
            onSave={vi.fn()}
            onCancel={onCancel}
          />,
        )
        fireEvent.click(screen.getByTestId('github-close'))

        // Assert
        expect(onCancel).toHaveBeenCalledTimes(1)
      })

      it('should call onSave when onSuccess is triggered', () => {
        // Arrange
        const onSave = vi.fn()

        // Act
        render(
          <FromGitHub
            payload={createMockGitHubPayload()}
            onSave={onSave}
            onCancel={vi.fn()}
          />,
        )
        fireEvent.click(screen.getByTestId('github-success'))

        // Assert
        expect(onSave).toHaveBeenCalledTimes(1)
      })
    })
  })

  // ============================================================
  // UpdateFromMarketplace (from-market-place.tsx) Tests
  // ============================================================
  describe('UpdateFromMarketplace (from-market-place.tsx)', () => {
    describe('Rendering', () => {
      it('should render modal with title and description', () => {
        // Arrange
        const payload = createMockMarketPlacePayload()

        // Act
        renderWithQueryClient(
          <UpdateFromMarketplace
            payload={payload}
            onSave={vi.fn()}
            onCancel={vi.fn()}
          />,
        )

        // Assert
        expect(screen.getByText('plugin.upgrade.title')).toBeInTheDocument()
        expect(screen.getByText('plugin.upgrade.description')).toBeInTheDocument()
      })

      it('should render version badge with version transition', () => {
        // Arrange
        const payload = createMockMarketPlacePayload({
          originalPackageInfo: {
            id: 'original-id',
            payload: createMockPluginDeclaration({ version: '1.0.0' }),
          },
          targetPackageInfo: {
            id: 'target-id',
            version: '2.0.0',
          },
        })

        // Act
        renderWithQueryClient(
          <UpdateFromMarketplace
            payload={payload}
            onSave={vi.fn()}
            onCancel={vi.fn()}
          />,
        )

        // Assert
        expect(screen.getByText('1.0.0 -> 2.0.0')).toBeInTheDocument()
      })

      it('should render Update button in initial state', () => {
        // Arrange
        const payload = createMockMarketPlacePayload()

        // Act
        renderWithQueryClient(
          <UpdateFromMarketplace
            payload={payload}
            onSave={vi.fn()}
            onCancel={vi.fn()}
          />,
        )

        // Assert
        expect(screen.getByRole('button', { name: 'plugin.upgrade.upgrade' })).toBeInTheDocument()
        expect(screen.getByRole('button', { name: 'common.operation.cancel' })).toBeInTheDocument()
      })
    })

    describe('Downgrade Warning Modal', () => {
      it('should show downgrade warning modal when isShowDowngradeWarningModal is true', () => {
        // Arrange
        const payload = createMockMarketPlacePayload()

        // Act
        renderWithQueryClient(
          <UpdateFromMarketplace
            payload={payload}
            onSave={vi.fn()}
            onCancel={vi.fn()}
            isShowDowngradeWarningModal={true}
          />,
        )

        // Assert
        expect(screen.getByText('plugin.autoUpdate.pluginDowngradeWarning.title')).toBeInTheDocument()
        expect(screen.getByText('plugin.autoUpdate.pluginDowngradeWarning.description')).toBeInTheDocument()
      })

      it('should not show downgrade warning modal when isShowDowngradeWarningModal is false', () => {
        // Arrange
        const payload = createMockMarketPlacePayload()

        // Act
        renderWithQueryClient(
          <UpdateFromMarketplace
            payload={payload}
            onSave={vi.fn()}
            onCancel={vi.fn()}
            isShowDowngradeWarningModal={false}
          />,
        )

        // Assert
        expect(screen.queryByText('plugin.autoUpdate.pluginDowngradeWarning.title')).not.toBeInTheDocument()
        expect(screen.getByText('plugin.upgrade.title')).toBeInTheDocument()
      })
    })

    describe('User Interactions', () => {
      it('should call onCancel when Cancel button is clicked', () => {
        // Arrange
        const onCancel = vi.fn()
        const payload = createMockMarketPlacePayload()

        // Act
        renderWithQueryClient(
          <UpdateFromMarketplace
            payload={payload}
            onSave={vi.fn()}
            onCancel={onCancel}
          />,
        )
        fireEvent.click(screen.getByRole('button', { name: 'common.operation.cancel' }))

        // Assert
        expect(onCancel).toHaveBeenCalledTimes(1)
      })

      it('should call updateFromMarketPlace API when Update button is clicked', async () => {
        // Arrange
        mockUpdateFromMarketPlace.mockResolvedValue({
          all_installed: true,
          task_id: 'task-123',
        })
        const onSave = vi.fn()
        const payload = createMockMarketPlacePayload()

        // Act
        renderWithQueryClient(
          <UpdateFromMarketplace
            payload={payload}
            onSave={onSave}
            onCancel={vi.fn()}
          />,
        )
        fireEvent.click(screen.getByRole('button', { name: 'plugin.upgrade.upgrade' }))

        // Assert
        await waitFor(() => {
          expect(mockUpdateFromMarketPlace).toHaveBeenCalledWith({
            original_plugin_unique_identifier: 'original-id',
            new_plugin_unique_identifier: 'test-target-id',
          })
        })
      })

      it('should show loading state during upgrade', async () => {
        // Arrange
        mockUpdateFromMarketPlace.mockImplementation(() => new Promise(() => {})) // Never resolves
        const payload = createMockMarketPlacePayload()

        // Act
        renderWithQueryClient(
          <UpdateFromMarketplace
            payload={payload}
            onSave={vi.fn()}
            onCancel={vi.fn()}
          />,
        )

        // Assert - button should show Update before clicking
        expect(screen.getByRole('button', { name: 'plugin.upgrade.upgrade' })).toBeInTheDocument()

        // Act - click update button
        fireEvent.click(screen.getByRole('button', { name: 'plugin.upgrade.upgrade' }))

        // Assert - Cancel button should be hidden during upgrade
        await waitFor(() => {
          expect(screen.queryByRole('button', { name: 'common.operation.cancel' })).not.toBeInTheDocument()
        })
      })

      it('should call onSave when update completes with all_installed true', async () => {
        // Arrange
        mockUpdateFromMarketPlace.mockResolvedValue({
          all_installed: true,
          task_id: 'task-123',
        })
        const onSave = vi.fn()
        const payload = createMockMarketPlacePayload()

        // Act
        renderWithQueryClient(
          <UpdateFromMarketplace
            payload={payload}
            onSave={onSave}
            onCancel={vi.fn()}
          />,
        )
        fireEvent.click(screen.getByRole('button', { name: 'plugin.upgrade.upgrade' }))

        // Assert
        await waitFor(() => {
          expect(onSave).toHaveBeenCalled()
        })
      })

      it('should check task status when all_installed is false', async () => {
        // Arrange
        mockUpdateFromMarketPlace.mockResolvedValue({
          all_installed: false,
          task_id: 'task-123',
        })
        mockCheck.mockResolvedValue({ status: TaskStatus.success })
        const onSave = vi.fn()
        const payload = createMockMarketPlacePayload()

        // Act
        renderWithQueryClient(
          <UpdateFromMarketplace
            payload={payload}
            onSave={onSave}
            onCancel={vi.fn()}
          />,
        )
        fireEvent.click(screen.getByRole('button', { name: 'plugin.upgrade.upgrade' }))

        // Assert
        await waitFor(() => {
          expect(mockHandleInstallTaskStart).toHaveBeenCalledWith({
            all_installed: false,
            task_id: 'task-123',
          })
        })
        await waitFor(() => {
          expect(mockCheck).toHaveBeenCalledWith({
            taskId: 'task-123',
            pluginUniqueIdentifier: 'test-target-id',
          })
        })
      })

      it('should stop task check and call onCancel when modal is cancelled during upgrade', () => {
        // Arrange
        const onCancel = vi.fn()
        const payload = createMockMarketPlacePayload()

        // Act
        renderWithQueryClient(
          <UpdateFromMarketplace
            payload={payload}
            onSave={vi.fn()}
            onCancel={onCancel}
          />,
        )
        fireEvent.click(screen.getByRole('button', { name: 'common.operation.cancel' }))

        // Assert
        expect(mockStop).toHaveBeenCalled()
        expect(onCancel).toHaveBeenCalled()
      })
    })

    describe('Error Handling', () => {
      it('should reset to notStarted state when API call fails', async () => {
        // Arrange
        mockUpdateFromMarketPlace.mockRejectedValue(new Error('API Error'))
        const payload = createMockMarketPlacePayload()

        // Act
        renderWithQueryClient(
          <UpdateFromMarketplace
            payload={payload}
            onSave={vi.fn()}
            onCancel={vi.fn()}
          />,
        )
        fireEvent.click(screen.getByRole('button', { name: 'plugin.upgrade.upgrade' }))

        // Assert
        await waitFor(() => {
          expect(screen.getByRole('button', { name: 'plugin.upgrade.upgrade' })).toBeInTheDocument()
        })
      })

      it('should reset loading state when task status check fails', async () => {
        // Arrange
        mockUpdateFromMarketPlace.mockResolvedValue({
          all_installed: false,
          task_id: 'task-123',
        })
        mockCheck.mockResolvedValue({
          status: TaskStatus.failed,
          error: 'Installation failed due to dependency conflict',
        })
        const onSave = vi.fn()
        const payload = createMockMarketPlacePayload()

        // Act
        renderWithQueryClient(
          <UpdateFromMarketplace
            payload={payload}
            onSave={onSave}
            onCancel={vi.fn()}
          />,
        )
        fireEvent.click(screen.getByRole('button', { name: 'plugin.upgrade.upgrade' }))

        // Assert
        await waitFor(() => {
          expect(mockCheck).toHaveBeenCalled()
        })
        await waitFor(() => {
          expect(toastErrorSpy).toHaveBeenCalledWith('Installation failed due to dependency conflict')
        })
        // onSave should NOT be called when task fails
        expect(onSave).not.toHaveBeenCalled()
        await waitFor(() => {
          expect(screen.getByRole('button', { name: 'plugin.upgrade.upgrade' })).toBeInTheDocument()
        })
        expect(screen.getByRole('button', { name: 'common.operation.cancel' })).toBeInTheDocument()
      })

      it('should stop loading when upgrade API returns failed task directly', async () => {
        // Arrange
        mockUpdateFromMarketPlace.mockResolvedValue({
          task: {
            status: TaskStatus.failed,
            plugins: [{
              plugin_unique_identifier: 'test-target-id',
              status: TaskStatus.failed,
              message: 'failed to init environment',
            }],
          },
        })
        const onSave = vi.fn()
        const payload = createMockMarketPlacePayload()

        // Act
        renderWithQueryClient(
          <UpdateFromMarketplace
            payload={payload}
            onSave={onSave}
            onCancel={vi.fn()}
          />,
        )
        fireEvent.click(screen.getByRole('button', { name: 'plugin.upgrade.upgrade' }))

        // Assert
        await waitFor(() => {
          expect(toastErrorSpy).toHaveBeenCalledWith('failed to init environment')
        })
        expect(mockCheck).not.toHaveBeenCalled()
        expect(onSave).not.toHaveBeenCalled()
        await waitFor(() => {
          expect(screen.getByRole('button', { name: 'plugin.upgrade.upgrade' })).toBeInTheDocument()
        })
        expect(screen.getByRole('button', { name: 'common.operation.cancel' })).toBeInTheDocument()
      })
    })

    describe('Component Memoization', () => {
      it('should be memoized with React.memo', () => {
        expect(UpdateFromMarketplace).toBeDefined()
        expect((UpdateFromMarketplace as { $$typeof?: symbol }).$$typeof?.toString()).toContain('Symbol')
      })
    })

    describe('Exclude and Downgrade', () => {
      it('should call mutateAsync and handleConfirm when exclude and downgrade is clicked', async () => {
        // Arrange
        mockMutateAsync.mockResolvedValue({})
        mockUpdateFromMarketPlace.mockResolvedValue({
          all_installed: true,
          task_id: 'task-123',
        })
        const payload = createMockMarketPlacePayload()

        // Act
        renderWithQueryClient(
          <UpdateFromMarketplace
            payload={payload}
            pluginId="test-plugin-id"
            onSave={vi.fn()}
            onCancel={vi.fn()}
            isShowDowngradeWarningModal={true}
          />,
        )
        fireEvent.click(screen.getByRole('button', { name: 'plugin.autoUpdate.pluginDowngradeWarning.exclude' }))

        // Assert
        await waitFor(() => {
          expect(mockMutateAsync).toHaveBeenCalledWith({
            plugin_id: 'test-plugin-id',
            category: PluginCategoryEnum.tool,
          })
        })
        await waitFor(() => {
          expect(mockUpdateFromMarketPlace).toHaveBeenCalled()
        })
      })

      it('should skip mutateAsync when pluginId is not provided', async () => {
        // Arrange - covers line 114 else branch
        mockMutateAsync.mockResolvedValue({})
        mockUpdateFromMarketPlace.mockResolvedValue({
          all_installed: true,
          task_id: 'task-123',
        })
        const payload = createMockMarketPlacePayload()

        // Act
        renderWithQueryClient(
          <UpdateFromMarketplace
            payload={payload}
            // pluginId is intentionally not provided
            onSave={vi.fn()}
            onCancel={vi.fn()}
            isShowDowngradeWarningModal={true}
          />,
        )
        fireEvent.click(screen.getByRole('button', { name: 'plugin.autoUpdate.pluginDowngradeWarning.exclude' }))

        // Assert - mutateAsync should NOT be called when pluginId is undefined
        await waitFor(() => {
          expect(mockUpdateFromMarketPlace).toHaveBeenCalled()
        })
        expect(mockMutateAsync).not.toHaveBeenCalled()
      })
    })
  })

  // ============================================================
  // DowngradeWarningModal (downgrade-warning.tsx) Tests
  // ============================================================
  describe('DowngradeWarningModal (downgrade-warning.tsx)', () => {
    describe('Rendering', () => {
      it('should render title and description', () => {
        // Act
        render(
          <DowngradeWarningModal
            onCancel={vi.fn()}
            onJustDowngrade={vi.fn()}
            onExcludeAndDowngrade={vi.fn()}
          />,
        )

        // Assert
        expect(screen.getByText('plugin.autoUpdate.pluginDowngradeWarning.title')).toBeInTheDocument()
        expect(screen.getByText('plugin.autoUpdate.pluginDowngradeWarning.description')).toBeInTheDocument()
      })

      it('should render all three action buttons', () => {
        // Act
        render(
          <DowngradeWarningModal
            onCancel={vi.fn()}
            onJustDowngrade={vi.fn()}
            onExcludeAndDowngrade={vi.fn()}
          />,
        )

        // Assert
        expect(screen.getByRole('button', { name: 'app.newApp.Cancel' })).toBeInTheDocument()
        expect(screen.getByRole('button', { name: 'plugin.autoUpdate.pluginDowngradeWarning.downgrade' })).toBeInTheDocument()
        expect(screen.getByRole('button', { name: 'plugin.autoUpdate.pluginDowngradeWarning.exclude' })).toBeInTheDocument()
      })
    })

    describe('User Interactions', () => {
      it('should call onCancel when Cancel button is clicked', () => {
        // Arrange
        const onCancel = vi.fn()

        // Act
        render(
          <DowngradeWarningModal
            onCancel={onCancel}
            onJustDowngrade={vi.fn()}
            onExcludeAndDowngrade={vi.fn()}
          />,
        )
        fireEvent.click(screen.getByRole('button', { name: 'app.newApp.Cancel' }))

        // Assert
        expect(onCancel).toHaveBeenCalledTimes(1)
      })

      it('should call onJustDowngrade when Just Downgrade button is clicked', () => {
        // Arrange
        const onJustDowngrade = vi.fn()

        // Act
        render(
          <DowngradeWarningModal
            onCancel={vi.fn()}
            onJustDowngrade={onJustDowngrade}
            onExcludeAndDowngrade={vi.fn()}
          />,
        )
        fireEvent.click(screen.getByRole('button', { name: 'plugin.autoUpdate.pluginDowngradeWarning.downgrade' }))

        // Assert
        expect(onJustDowngrade).toHaveBeenCalledTimes(1)
      })

      it('should call onExcludeAndDowngrade when Exclude and Downgrade button is clicked', () => {
        // Arrange
        const onExcludeAndDowngrade = vi.fn()

        // Act
        render(
          <DowngradeWarningModal
            onCancel={vi.fn()}
            onJustDowngrade={vi.fn()}
            onExcludeAndDowngrade={onExcludeAndDowngrade}
          />,
        )
        fireEvent.click(screen.getByRole('button', { name: 'plugin.autoUpdate.pluginDowngradeWarning.exclude' }))

        // Assert
        expect(onExcludeAndDowngrade).toHaveBeenCalledTimes(1)
      })
    })
  })

  // ============================================================
  // PluginVersionPicker (plugin-version-picker.tsx) Tests
  // ============================================================
  describe('PluginVersionPicker (plugin-version-picker.tsx)', () => {
    const defaultProps = {
      isShow: false,
      onShowChange: vi.fn(),
      pluginID: 'test-plugin-id',
      currentVersion: '1.0.0',
      trigger: <span>Select Version</span>,
      onSelect: vi.fn(),
    }

    describe('Rendering', () => {
      it('should render trigger element', () => {
        // Act
        render(<PluginVersionPicker {...defaultProps} />)

        // Assert
        expect(screen.getByText('Select Version')).toBeInTheDocument()
      })

      it('should not render content when isShow is false', () => {
        // Act
        render(<PluginVersionPicker {...defaultProps} isShow={false} />)

        // Assert
        expect(screen.queryByText('plugin.detailPanel.switchVersion')).not.toBeInTheDocument()
      })

      it('should render version list when isShow is true', () => {
        // Act
        render(<PluginVersionPicker {...defaultProps} isShow={true} />)

        // Assert
        expect(screen.getByText('plugin.detailPanel.switchVersion')).toBeInTheDocument()
      })

      it('should render all versions from API', () => {
        // Act
        render(<PluginVersionPicker {...defaultProps} isShow={true} />)

        // Assert
        expect(screen.getByText('1.0.0')).toBeInTheDocument()
        expect(screen.getByText('1.1.0')).toBeInTheDocument()
        expect(screen.getByText('2.0.0')).toBeInTheDocument()
      })

      it('should show CURRENT badge for current version', () => {
        // Act
        render(<PluginVersionPicker {...defaultProps} isShow={true} currentVersion="1.0.0" />)

        // Assert
        expect(screen.getByText('CURRENT')).toBeInTheDocument()
      })
    })

    describe('User Interactions', () => {
      it('should call onShowChange when trigger is clicked', () => {
        // Arrange
        const onShowChange = vi.fn()

        // Act
        render(<PluginVersionPicker {...defaultProps} onShowChange={onShowChange} />)
        fireEvent.click(screen.getByText('Select Version'))

        // Assert
        expect(onShowChange).toHaveBeenCalledWith(true)
      })

      it('should not call onShowChange when trigger is clicked and disabled is true', () => {
        // Arrange
        const onShowChange = vi.fn()

        // Act
        render(<PluginVersionPicker {...defaultProps} disabled={true} onShowChange={onShowChange} />)
        fireEvent.click(screen.getByText('Select Version'))

        // Assert
        expect(onShowChange).not.toHaveBeenCalled()
      })

      it('should call onSelect with correct params when a version is selected', () => {
        // Arrange
        const onSelect = vi.fn()
        const onShowChange = vi.fn()

        // Act
        render(
          <PluginVersionPicker
            {...defaultProps}
            isShow={true}
            currentVersion="1.0.0"
            onSelect={onSelect}
            onShowChange={onShowChange}
          />,
        )
        // Click on version 2.0.0
        const versionElements = screen.getAllByText(/^\d+\.\d+\.\d+$/)
        const version2Element = versionElements.find(el => el.textContent === '2.0.0')
        if (version2Element) {
          fireEvent.click(version2Element.closest('div[class*="cursor-pointer"]')!)
        }

        // Assert
        expect(onSelect).toHaveBeenCalledWith({
          version: '2.0.0',
          unique_identifier: 'plugin-v2.0.0',
          isDowngrade: false,
        })
        expect(onShowChange).toHaveBeenCalledWith(false)
      })

      it('should not call onSelect when clicking on current version', () => {
        // Arrange
        const onSelect = vi.fn()

        // Act
        render(
          <PluginVersionPicker
            {...defaultProps}
            isShow={true}
            currentVersion="1.0.0"
            onSelect={onSelect}
          />,
        )
        // Click on current version 1.0.0
        const versionElements = screen.getAllByText(/^\d+\.\d+\.\d+$/)
        const version1Element = versionElements.find(el => el.textContent === '1.0.0')
        if (version1Element) {
          fireEvent.click(version1Element.closest('div[class*="cursor"]')!)
        }

        // Assert
        expect(onSelect).not.toHaveBeenCalled()
      })

      it('should indicate downgrade when selecting a lower version', () => {
        // Arrange
        const onSelect = vi.fn()

        // Act
        render(
          <PluginVersionPicker
            {...defaultProps}
            isShow={true}
            currentVersion="2.0.0"
            onSelect={onSelect}
          />,
        )
        // Click on version 1.0.0 (downgrade)
        const versionElements = screen.getAllByText(/^\d+\.\d+\.\d+$/)
        const version1Element = versionElements.find(el => el.textContent === '1.0.0')
        if (version1Element) {
          fireEvent.click(version1Element.closest('div[class*="cursor-pointer"]')!)
        }

        // Assert
        expect(onSelect).toHaveBeenCalledWith({
          version: '1.0.0',
          unique_identifier: 'plugin-v1.0.0',
          isDowngrade: true,
        })
      })
    })

    describe('Props', () => {
      it('should support custom placement', () => {
        // Act
        render(
          <PluginVersionPicker
            {...defaultProps}
            isShow={true}
            placement="top-end"
          />,
        )

        // Assert
        expect(screen.getByText('plugin.detailPanel.switchVersion')).toBeInTheDocument()
      })

      it('should support custom offset', () => {
        // Act
        render(
          <PluginVersionPicker
            {...defaultProps}
            isShow={true}
            sideOffset={10}
            alignOffset={20}
          />,
        )

        // Assert
        expect(screen.getByText('plugin.detailPanel.switchVersion')).toBeInTheDocument()
      })
    })

    describe('Component Memoization', () => {
      it('should be memoized with React.memo', () => {
        expect(PluginVersionPicker).toBeDefined()
        expect((PluginVersionPicker as { $$typeof?: symbol }).$$typeof?.toString()).toContain('Symbol')
      })
    })
  })

  // ============================================================
  // Edge Cases
  // ============================================================
  describe('Edge Cases', () => {
    it('should render github update with undefined payload (mock handles it)', () => {
      // Arrange - the mocked InstallFromGitHub handles undefined payload
      const props: UpdatePluginModalType = {
        type: PluginSource.github,
        category: PluginCategoryEnum.tool,
        github: undefined as unknown as UpdateFromGitHubPayload,
        onCancel: vi.fn(),
        onSave: vi.fn(),
      }

      // Act
      render(<UpdatePlugin {...props} />)

      // Assert - mock component renders with undefined payload
      expect(screen.getByTestId('install-from-github')).toBeInTheDocument()
    })

    it('should throw error when marketplace payload is undefined', () => {
      // Arrange
      const props: UpdatePluginModalType = {
        type: PluginSource.marketplace,
        category: PluginCategoryEnum.tool,
        marketPlace: undefined as unknown as UpdateFromMarketPlacePayload,
        onCancel: vi.fn(),
        onSave: vi.fn(),
      }

      // Act & Assert - should throw because payload is required
      expect(() => renderWithQueryClient(<UpdatePlugin {...props} />)).toThrow()
    })

    it('should handle empty version list in PluginVersionPicker', () => {
      // Override the mock temporarily
      vi.mocked(vi.importActual('@/service/use-plugins') as unknown as Record<string, unknown>).useVersionListOfPlugin = () => ({
        data: { data: { versions: [] } },
      })

      // Act
      render(
        <PluginVersionPicker {...{
          isShow: true,
          onShowChange: vi.fn(),
          pluginID: 'test',
          currentVersion: '1.0.0',
          trigger: <span>Select</span>,
          onSelect: vi.fn(),
        }}
        />,
      )

      // Assert
      expect(screen.getByText('plugin.detailPanel.switchVersion')).toBeInTheDocument()
    })
  })
})
