import type { Plugin, PluginDeclaration, UpdateFromGitHubPayload } from '../../../../types'
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { PluginCategoryEnum, TaskStatus } from '../../../../types'
import Loaded from '../loaded'

// Mock dependencies
const mockUseCheckInstalled = vi.fn()
vi.mock('@/app/components/plugins/install-plugin/hooks/use-check-installed', () => ({
  default: (params: { pluginIds: string[], enabled: boolean }) => mockUseCheckInstalled(params),
}))

const mockUpdateFromGitHub = vi.fn()
vi.mock('@/service/plugins', () => ({
  updateFromGitHub: (...args: unknown[]) => mockUpdateFromGitHub(...args),
}))

const mockInstallPackageFromGitHub = vi.fn()
const mockHandleRefetch = vi.fn()
vi.mock('@/service/use-plugins', () => ({
  useInstallPackageFromGitHub: () => ({ mutateAsync: mockInstallPackageFromGitHub }),
  usePluginTaskList: () => ({ handleRefetch: mockHandleRefetch }),
}))

const mockCheck = vi.fn()
vi.mock('../../../base/check-task-status', () => ({
  default: () => ({ check: mockCheck }),
}))

// Mock Card component
vi.mock('../../../../card', () => ({
  default: ({ payload, titleLeft }: { payload: Plugin, titleLeft?: React.ReactNode }) => (
    <div data-testid="plugin-card">
      <span data-testid="card-name">{payload.name}</span>
      {!!titleLeft && <span data-testid="title-left">{titleLeft}</span>}
    </div>
  ),
}))

// Mock Version component
vi.mock('../../../base/version', () => ({
  default: ({ hasInstalled, installedVersion, toInstallVersion }: {
    hasInstalled: boolean
    installedVersion?: string
    toInstallVersion: string
  }) => (
    <span data-testid="version-info">
      {hasInstalled ? `Update from ${installedVersion} to ${toInstallVersion}` : `Install ${toInstallVersion}`}
    </span>
  ),
}))

// Factory functions
const createMockPayload = (overrides: Partial<PluginDeclaration> = {}): PluginDeclaration => ({
  plugin_unique_identifier: 'test-uid',
  version: '1.0.0',
  author: 'test-author',
  icon: 'icon.png',
  name: 'Test Plugin',
  category: PluginCategoryEnum.tool,
  label: { 'en-US': 'Test' } as PluginDeclaration['label'],
  description: { 'en-US': 'Test Description' } as PluginDeclaration['description'],
  created_at: '2024-01-01',
  resource: {},
  plugins: [],
  verified: true,
  endpoint: { settings: [], endpoints: [] },
  model: null,
  tags: [],
  agent_strategy: null,
  meta: { version: '1.0.0' },
  trigger: {} as PluginDeclaration['trigger'],
  ...overrides,
})

const createMockPluginPayload = (overrides: Partial<Plugin> = {}): Plugin => ({
  type: 'plugin',
  org: 'test-org',
  name: 'Test Plugin',
  plugin_id: 'test-plugin-id',
  version: '1.0.0',
  latest_version: '1.0.0',
  latest_package_identifier: 'test-pkg',
  icon: 'icon.png',
  verified: true,
  label: { 'en-US': 'Test' },
  brief: { 'en-US': 'Brief' },
  description: { 'en-US': 'Description' },
  introduction: 'Intro',
  repository: '',
  category: PluginCategoryEnum.tool,
  install_count: 100,
  endpoint: { settings: [] },
  tags: [],
  badges: [],
  verification: { authorized_category: 'langgenius' },
  from: 'github',
  ...overrides,
})

const createUpdatePayload = (): UpdateFromGitHubPayload => ({
  originalPackageInfo: {
    id: 'original-id',
    repo: 'owner/repo',
    version: 'v0.9.0',
    package: 'plugin.zip',
    releases: [],
  },
})

describe('Loaded', () => {
  const defaultProps = {
    updatePayload: undefined,
    uniqueIdentifier: 'test-unique-id',
    payload: createMockPayload() as PluginDeclaration | Plugin,
    repoUrl: 'https://github.com/owner/repo',
    selectedVersion: 'v1.0.0',
    selectedPackage: 'plugin.zip',
    onBack: vi.fn(),
    onStartToInstall: vi.fn(),
    onInstalled: vi.fn(),
    onFailed: vi.fn(),
  }

  beforeEach(() => {
    vi.clearAllMocks()
    mockUseCheckInstalled.mockReturnValue({
      installedInfo: {},
      isLoading: false,
    })
    mockUpdateFromGitHub.mockResolvedValue({ all_installed: true, task_id: 'task-1' })
    mockInstallPackageFromGitHub.mockResolvedValue({ all_installed: true, task_id: 'task-1' })
    mockCheck.mockResolvedValue({ status: TaskStatus.success, error: null })
  })

  // ================================
  // Rendering Tests
  // ================================
  describe('Rendering', () => {
    it('should render ready to install message', () => {
      render(<Loaded {...defaultProps} />)

      expect(screen.getByText('plugin.installModal.readyToInstall')).toBeInTheDocument()
    })

    it('should render plugin card', () => {
      render(<Loaded {...defaultProps} />)

      expect(screen.getByTestId('plugin-card')).toBeInTheDocument()
    })

    it('should render back button when not installing', () => {
      render(<Loaded {...defaultProps} />)

      expect(screen.getByRole('button', { name: 'plugin.installModal.back' })).toBeInTheDocument()
    })

    it('should render install button', () => {
      render(<Loaded {...defaultProps} />)

      expect(screen.getByRole('button', { name: /plugin.installModal.install/i })).toBeInTheDocument()
    })

    it('should show version info in card title', () => {
      render(<Loaded {...defaultProps} />)

      expect(screen.getByTestId('version-info')).toBeInTheDocument()
    })
  })

  // ================================
  // Props Tests
  // ================================
  describe('Props', () => {
    it('should display plugin name from payload', () => {
      render(<Loaded {...defaultProps} />)

      expect(screen.getByTestId('card-name')).toHaveTextContent('Test Plugin')
    })

    it('should pass correct version to Version component', () => {
      render(<Loaded {...defaultProps} payload={createMockPayload({ version: '2.0.0' })} />)

      expect(screen.getByTestId('version-info')).toHaveTextContent('Install 2.0.0')
    })
  })

  // ================================
  // Button State Tests
  // ================================
  describe('Button State', () => {
    it('should disable install button while loading', () => {
      mockUseCheckInstalled.mockReturnValue({
        installedInfo: {},
        isLoading: true,
      })

      render(<Loaded {...defaultProps} />)

      expect(screen.getByRole('button', { name: /plugin.installModal.install/i })).toBeDisabled()
    })

    it('should enable install button when not loading', () => {
      render(<Loaded {...defaultProps} />)

      expect(screen.getByRole('button', { name: /plugin.installModal.install/i })).not.toBeDisabled()
    })
  })

  // ================================
  // User Interactions Tests
  // ================================
  describe('User Interactions', () => {
    it('should call onBack when back button is clicked', () => {
      const onBack = vi.fn()
      render(<Loaded {...defaultProps} onBack={onBack} />)

      fireEvent.click(screen.getByRole('button', { name: 'plugin.installModal.back' }))

      expect(onBack).toHaveBeenCalledTimes(1)
    })

    it('should call onStartToInstall when install starts', async () => {
      const onStartToInstall = vi.fn()
      render(<Loaded {...defaultProps} onStartToInstall={onStartToInstall} />)

      fireEvent.click(screen.getByRole('button', { name: /plugin.installModal.install/i }))

      await waitFor(() => {
        expect(onStartToInstall).toHaveBeenCalledTimes(1)
      })
    })
  })

  // ================================
  // Installation Flow Tests
  // ================================
  describe('Installation Flows', () => {
    it('should call installPackageFromGitHub for fresh install', async () => {
      const onInstalled = vi.fn()
      render(<Loaded {...defaultProps} onInstalled={onInstalled} />)

      fireEvent.click(screen.getByRole('button', { name: /plugin.installModal.install/i }))

      await waitFor(() => {
        expect(mockInstallPackageFromGitHub).toHaveBeenCalledWith({
          repoUrl: 'owner/repo',
          selectedVersion: 'v1.0.0',
          selectedPackage: 'plugin.zip',
          uniqueIdentifier: 'test-unique-id',
        })
      })
    })

    it('should call updateFromGitHub when updatePayload is provided', async () => {
      const updatePayload = createUpdatePayload()
      render(<Loaded {...defaultProps} updatePayload={updatePayload} />)

      fireEvent.click(screen.getByRole('button', { name: /plugin.installModal.install/i }))

      await waitFor(() => {
        expect(mockUpdateFromGitHub).toHaveBeenCalledWith(
          'owner/repo',
          'v1.0.0',
          'plugin.zip',
          'original-id',
          'test-unique-id',
        )
      })
    })

    it('should call updateFromGitHub when plugin is already installed', async () => {
      mockUseCheckInstalled.mockReturnValue({
        installedInfo: {
          'test-plugin-id': {
            installedVersion: '0.9.0',
            uniqueIdentifier: 'installed-uid',
          },
        },
        isLoading: false,
      })

      render(<Loaded {...defaultProps} payload={createMockPluginPayload()} />)

      fireEvent.click(screen.getByRole('button', { name: /plugin.installModal.install/i }))

      await waitFor(() => {
        expect(mockUpdateFromGitHub).toHaveBeenCalledWith(
          'owner/repo',
          'v1.0.0',
          'plugin.zip',
          'installed-uid',
          'test-unique-id',
        )
      })
    })

    it('should call onInstalled when installation completes immediately', async () => {
      mockInstallPackageFromGitHub.mockResolvedValue({ all_installed: true, task_id: 'task-1' })

      const onInstalled = vi.fn()
      render(<Loaded {...defaultProps} onInstalled={onInstalled} />)

      fireEvent.click(screen.getByRole('button', { name: /plugin.installModal.install/i }))

      await waitFor(() => {
        expect(onInstalled).toHaveBeenCalled()
      })
    })

    it('should check task status when not immediately installed', async () => {
      mockInstallPackageFromGitHub.mockResolvedValue({ all_installed: false, task_id: 'task-1' })

      render(<Loaded {...defaultProps} />)

      fireEvent.click(screen.getByRole('button', { name: /plugin.installModal.install/i }))

      await waitFor(() => {
        expect(mockHandleRefetch).toHaveBeenCalled()
        expect(mockCheck).toHaveBeenCalledWith({
          taskId: 'task-1',
          pluginUniqueIdentifier: 'test-unique-id',
        })
      })
    })

    it('should call onInstalled with true when task succeeds', async () => {
      mockInstallPackageFromGitHub.mockResolvedValue({ all_installed: false, task_id: 'task-1' })
      mockCheck.mockResolvedValue({ status: TaskStatus.success, error: null })

      const onInstalled = vi.fn()
      render(<Loaded {...defaultProps} onInstalled={onInstalled} />)

      fireEvent.click(screen.getByRole('button', { name: /plugin.installModal.install/i }))

      await waitFor(() => {
        expect(onInstalled).toHaveBeenCalledWith(true)
      })
    })
  })

  // ================================
  // Error Handling Tests
  // ================================
  describe('Error Handling', () => {
    it('should call onFailed when task fails', async () => {
      mockInstallPackageFromGitHub.mockResolvedValue({ all_installed: false, task_id: 'task-1' })
      mockCheck.mockResolvedValue({ status: TaskStatus.failed, error: 'Installation failed' })

      const onFailed = vi.fn()
      render(<Loaded {...defaultProps} onFailed={onFailed} />)

      fireEvent.click(screen.getByRole('button', { name: /plugin.installModal.install/i }))

      await waitFor(() => {
        expect(onFailed).toHaveBeenCalledWith('Installation failed')
      })
    })

    it('should call onFailed with string error', async () => {
      mockInstallPackageFromGitHub.mockRejectedValue('String error message')

      const onFailed = vi.fn()
      render(<Loaded {...defaultProps} onFailed={onFailed} />)

      fireEvent.click(screen.getByRole('button', { name: /plugin.installModal.install/i }))

      await waitFor(() => {
        expect(onFailed).toHaveBeenCalledWith('String error message')
      })
    })

    it('should call onFailed without message for non-string errors', async () => {
      mockInstallPackageFromGitHub.mockRejectedValue(new Error('Error object'))

      const onFailed = vi.fn()
      render(<Loaded {...defaultProps} onFailed={onFailed} />)

      fireEvent.click(screen.getByRole('button', { name: /plugin.installModal.install/i }))

      await waitFor(() => {
        expect(onFailed).toHaveBeenCalledWith()
      })
    })
  })

  // ================================
  // Auto-install Effect Tests
  // ================================
  describe('Auto-install Effect', () => {
    it('should call onInstalled when already installed with same identifier', () => {
      mockUseCheckInstalled.mockReturnValue({
        installedInfo: {
          'test-plugin-id': {
            installedVersion: '1.0.0',
            uniqueIdentifier: 'test-unique-id',
          },
        },
        isLoading: false,
      })

      const onInstalled = vi.fn()
      render(<Loaded {...defaultProps} payload={createMockPluginPayload()} onInstalled={onInstalled} />)

      expect(onInstalled).toHaveBeenCalled()
    })

    it('should not call onInstalled when identifiers differ', () => {
      mockUseCheckInstalled.mockReturnValue({
        installedInfo: {
          'test-plugin-id': {
            installedVersion: '1.0.0',
            uniqueIdentifier: 'different-uid',
          },
        },
        isLoading: false,
      })

      const onInstalled = vi.fn()
      render(<Loaded {...defaultProps} payload={createMockPluginPayload()} onInstalled={onInstalled} />)

      expect(onInstalled).not.toHaveBeenCalled()
    })
  })

  // ================================
  // Installing State Tests
  // ================================
  describe('Installing State', () => {
    it('should hide back button while installing', async () => {
      let resolveInstall: (value: { all_installed: boolean, task_id: string }) => void
      mockInstallPackageFromGitHub.mockImplementation(() => new Promise((resolve) => {
        resolveInstall = resolve
      }))

      render(<Loaded {...defaultProps} />)

      fireEvent.click(screen.getByRole('button', { name: /plugin.installModal.install/i }))

      await waitFor(() => {
        expect(screen.queryByRole('button', { name: 'plugin.installModal.back' })).not.toBeInTheDocument()
      })

      resolveInstall!({ all_installed: true, task_id: 'task-1' })
    })

    it('should show installing text while installing', async () => {
      let resolveInstall: (value: { all_installed: boolean, task_id: string }) => void
      mockInstallPackageFromGitHub.mockImplementation(() => new Promise((resolve) => {
        resolveInstall = resolve
      }))

      render(<Loaded {...defaultProps} />)

      fireEvent.click(screen.getByRole('button', { name: /plugin.installModal.install/i }))

      await waitFor(() => {
        expect(screen.getByText('plugin.installModal.installing')).toBeInTheDocument()
      })

      resolveInstall!({ all_installed: true, task_id: 'task-1' })
    })

    it('should not trigger install twice when already installing', async () => {
      let resolveInstall: (value: { all_installed: boolean, task_id: string }) => void
      mockInstallPackageFromGitHub.mockImplementation(() => new Promise((resolve) => {
        resolveInstall = resolve
      }))

      render(<Loaded {...defaultProps} />)

      const installButton = screen.getByRole('button', { name: /plugin.installModal.install/i })

      // Click twice
      fireEvent.click(installButton)
      fireEvent.click(installButton)

      await waitFor(() => {
        expect(mockInstallPackageFromGitHub).toHaveBeenCalledTimes(1)
      })

      resolveInstall!({ all_installed: true, task_id: 'task-1' })
    })
  })

  // ================================
  // Edge Cases Tests
  // ================================
  describe('Edge Cases', () => {
    it('should handle missing onStartToInstall callback', async () => {
      render(<Loaded {...defaultProps} onStartToInstall={undefined} />)

      // Should not throw when callback is undefined
      expect(() => {
        fireEvent.click(screen.getByRole('button', { name: /plugin.installModal.install/i }))
      }).not.toThrow()

      await waitFor(() => {
        expect(mockInstallPackageFromGitHub).toHaveBeenCalled()
      })
    })

    it('should handle plugin without plugin_id', () => {
      mockUseCheckInstalled.mockReturnValue({
        installedInfo: {},
        isLoading: false,
      })

      render(<Loaded {...defaultProps} payload={createMockPayload()} />)

      expect(mockUseCheckInstalled).toHaveBeenCalledWith({
        pluginIds: [undefined],
        enabled: false,
      })
    })

    it('should preserve state after component update', () => {
      const { rerender } = render(<Loaded {...defaultProps} />)

      rerender(<Loaded {...defaultProps} />)

      expect(screen.getByTestId('plugin-card')).toBeInTheDocument()
    })
  })
})
