import { fireEvent, render, screen, waitFor } from '@testing-library/react'
import React from 'react'
import OnlineDrive from './index'
import Header from './header'
import { convertOnlineDriveData, isBucketListInitiation, isFile } from './utils'
import type { OnlineDriveFile } from '@/models/pipeline'
import { DatasourceType, OnlineDriveFileType } from '@/models/pipeline'
import type { DataSourceNodeType } from '@/app/components/workflow/nodes/data-source/types'
import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants'
import type { OnlineDriveData } from '@/types/pipeline'

// ==========================================
// Mock Modules
// ==========================================

// Note: react-i18next uses global mock from web/__mocks__/react-i18next.ts

// Mock useDocLink - context hook requires mocking
const mockDocLink = jest.fn((path?: string) => `https://docs.example.com${path || ''}`)
jest.mock('@/context/i18n', () => ({
  useDocLink: () => mockDocLink,
}))

// Mock dataset-detail context - context provider requires mocking
let mockPipelineId: string | undefined = 'pipeline-123'
jest.mock('@/context/dataset-detail', () => ({
  useDatasetDetailContextWithSelector: (selector: (s: any) => any) => selector({ dataset: { pipeline_id: mockPipelineId } }),
}))

// Mock modal context - context provider requires mocking
const mockSetShowAccountSettingModal = jest.fn()
jest.mock('@/context/modal-context', () => ({
  useModalContextSelector: (selector: (s: any) => any) => selector({ setShowAccountSettingModal: mockSetShowAccountSettingModal }),
}))

// Mock ssePost - API service requires mocking
const mockSsePost = jest.fn()
jest.mock('@/service/base', () => ({
  ssePost: (...args: any[]) => mockSsePost(...args),
}))

// Mock useGetDataSourceAuth - API service hook requires mocking
const mockUseGetDataSourceAuth = jest.fn()
jest.mock('@/service/use-datasource', () => ({
  useGetDataSourceAuth: (params: any) => mockUseGetDataSourceAuth(params),
}))

// Mock Toast
const mockToastNotify = jest.fn()
jest.mock('@/app/components/base/toast', () => ({
  __esModule: true,
  default: {
    notify: (...args: any[]) => mockToastNotify(...args),
  },
}))

// Note: zustand/react/shallow useShallow is imported directly (simple utility function)

// Mock store state
const mockStoreState = {
  nextPageParameters: {} as Record<string, any>,
  breadcrumbs: [] as string[],
  prefix: [] as string[],
  keywords: '',
  bucket: '',
  selectedFileIds: [] as string[],
  onlineDriveFileList: [] as OnlineDriveFile[],
  currentCredentialId: '',
  isTruncated: { current: false },
  currentNextPageParametersRef: { current: {} },
  setOnlineDriveFileList: jest.fn(),
  setKeywords: jest.fn(),
  setSelectedFileIds: jest.fn(),
  setBreadcrumbs: jest.fn(),
  setPrefix: jest.fn(),
  setBucket: jest.fn(),
  setHasBucket: jest.fn(),
}

const mockGetState = jest.fn(() => mockStoreState)
const mockDataSourceStore = { getState: mockGetState }

jest.mock('../store', () => ({
  useDataSourceStoreWithSelector: (selector: (s: any) => any) => selector(mockStoreState),
  useDataSourceStore: () => mockDataSourceStore,
}))

// Mock Header component
jest.mock('../base/header', () => {
  const MockHeader = (props: any) => (
    <div data-testid="header">
      <span data-testid="header-doc-title">{props.docTitle}</span>
      <span data-testid="header-doc-link">{props.docLink}</span>
      <span data-testid="header-plugin-name">{props.pluginName}</span>
      <span data-testid="header-credential-id">{props.currentCredentialId}</span>
      <button data-testid="header-config-btn" onClick={props.onClickConfiguration}>Configure</button>
      <button data-testid="header-credential-change" onClick={() => props.onCredentialChange('new-cred-id')}>Change Credential</button>
      <span data-testid="header-credentials-count">{props.credentials?.length || 0}</span>
    </div>
  )
  return MockHeader
})

// Mock FileList component
jest.mock('./file-list', () => {
  const MockFileList = (props: any) => (
    <div data-testid="file-list">
      <span data-testid="file-list-count">{props.fileList?.length || 0}</span>
      <span data-testid="file-list-selected-count">{props.selectedFileIds?.length || 0}</span>
      <span data-testid="file-list-breadcrumbs">{props.breadcrumbs?.join('/') || ''}</span>
      <span data-testid="file-list-keywords">{props.keywords}</span>
      <span data-testid="file-list-bucket">{props.bucket}</span>
      <span data-testid="file-list-loading">{String(props.isLoading)}</span>
      <span data-testid="file-list-is-in-pipeline">{String(props.isInPipeline)}</span>
      <span data-testid="file-list-support-batch">{String(props.supportBatchUpload)}</span>
      <input
        data-testid="file-list-search-input"
        onChange={e => props.updateKeywords(e.target.value)}
      />
      <button data-testid="file-list-reset-keywords" onClick={props.resetKeywords}>Reset</button>
      <button
        data-testid="file-list-select-file"
        onClick={() => {
          const file: OnlineDriveFile = { id: 'file-1', name: 'test.txt', type: OnlineDriveFileType.file }
          props.handleSelectFile(file)
        }}
      >
        Select File
      </button>
      <button
        data-testid="file-list-select-bucket"
        onClick={() => {
          const file: OnlineDriveFile = { id: 'bucket-1', name: 'my-bucket', type: OnlineDriveFileType.bucket }
          props.handleSelectFile(file)
        }}
      >
        Select Bucket
      </button>
      <button
        data-testid="file-list-open-folder"
        onClick={() => {
          const file: OnlineDriveFile = { id: 'folder-1', name: 'my-folder', type: OnlineDriveFileType.folder }
          props.handleOpenFolder(file)
        }}
      >
        Open Folder
      </button>
      <button
        data-testid="file-list-open-bucket"
        onClick={() => {
          const file: OnlineDriveFile = { id: 'bucket-1', name: 'my-bucket', type: OnlineDriveFileType.bucket }
          props.handleOpenFolder(file)
        }}
      >
        Open Bucket
      </button>
      <button
        data-testid="file-list-open-file"
        onClick={() => {
          const file: OnlineDriveFile = { id: 'file-1', name: 'test.txt', type: OnlineDriveFileType.file }
          props.handleOpenFolder(file)
        }}
      >
        Open File
      </button>
    </div>
  )
  return MockFileList
})

// ==========================================
// Test Data Builders
// ==========================================
const createMockNodeData = (overrides?: Partial<DataSourceNodeType>): DataSourceNodeType => ({
  title: 'Test Node',
  plugin_id: 'plugin-123',
  provider_type: 'online_drive',
  provider_name: 'online-drive-provider',
  datasource_name: 'online-drive-ds',
  datasource_label: 'Online Drive',
  datasource_parameters: {},
  datasource_configurations: {},
  ...overrides,
} as DataSourceNodeType)

const createMockOnlineDriveFile = (overrides?: Partial<OnlineDriveFile>): OnlineDriveFile => ({
  id: 'file-1',
  name: 'test-file.txt',
  size: 1024,
  type: OnlineDriveFileType.file,
  ...overrides,
})

const createMockCredential = (overrides?: Partial<{ id: string; name: string }>) => ({
  id: 'cred-1',
  name: 'Test Credential',
  avatar_url: 'https://example.com/avatar.png',
  credential: {},
  is_default: false,
  type: 'oauth2',
  ...overrides,
})

type OnlineDriveProps = React.ComponentProps<typeof OnlineDrive>

const createDefaultProps = (overrides?: Partial<OnlineDriveProps>): OnlineDriveProps => ({
  nodeId: 'node-1',
  nodeData: createMockNodeData(),
  onCredentialChange: jest.fn(),
  isInPipeline: false,
  supportBatchUpload: true,
  ...overrides,
})

// ==========================================
// Helper Functions
// ==========================================
const resetMockStoreState = () => {
  mockStoreState.nextPageParameters = {}
  mockStoreState.breadcrumbs = []
  mockStoreState.prefix = []
  mockStoreState.keywords = ''
  mockStoreState.bucket = ''
  mockStoreState.selectedFileIds = []
  mockStoreState.onlineDriveFileList = []
  mockStoreState.currentCredentialId = ''
  mockStoreState.isTruncated = { current: false }
  mockStoreState.currentNextPageParametersRef = { current: {} }
  mockStoreState.setOnlineDriveFileList = jest.fn()
  mockStoreState.setKeywords = jest.fn()
  mockStoreState.setSelectedFileIds = jest.fn()
  mockStoreState.setBreadcrumbs = jest.fn()
  mockStoreState.setPrefix = jest.fn()
  mockStoreState.setBucket = jest.fn()
  mockStoreState.setHasBucket = jest.fn()
}

// ==========================================
// Test Suites
// ==========================================
describe('OnlineDrive', () => {
  beforeEach(() => {
    jest.clearAllMocks()

    // Reset store state
    resetMockStoreState()

    // Reset context values
    mockPipelineId = 'pipeline-123'
    mockSetShowAccountSettingModal.mockClear()

    // Default mock return values
    mockUseGetDataSourceAuth.mockReturnValue({
      data: { result: [createMockCredential()] },
    })

    mockGetState.mockReturnValue(mockStoreState)
  })

  // ==========================================
  // Rendering Tests
  // ==========================================
  describe('Rendering', () => {
    it('should render without crashing', () => {
      // Arrange
      const props = createDefaultProps()

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

      // Assert
      expect(screen.getByTestId('header')).toBeInTheDocument()
      expect(screen.getByTestId('file-list')).toBeInTheDocument()
    })

    it('should render Header with correct props', () => {
      // Arrange
      mockStoreState.currentCredentialId = 'cred-123'
      const props = createDefaultProps({
        nodeData: createMockNodeData({ datasource_label: 'My Online Drive' }),
      })

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

      // Assert
      expect(screen.getByTestId('header-doc-title')).toHaveTextContent('Docs')
      expect(screen.getByTestId('header-plugin-name')).toHaveTextContent('My Online Drive')
      expect(screen.getByTestId('header-credential-id')).toHaveTextContent('cred-123')
    })

    it('should render FileList with correct props', () => {
      // Arrange
      mockStoreState.currentCredentialId = 'cred-1'
      mockStoreState.keywords = 'search-term'
      mockStoreState.breadcrumbs = ['folder1', 'folder2']
      mockStoreState.bucket = 'my-bucket'
      mockStoreState.selectedFileIds = ['file-1', 'file-2']
      mockStoreState.onlineDriveFileList = [
        createMockOnlineDriveFile({ id: 'file-1', name: 'file1.txt' }),
        createMockOnlineDriveFile({ id: 'file-2', name: 'file2.txt' }),
      ]
      const props = createDefaultProps()

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

      // Assert
      expect(screen.getByTestId('file-list')).toBeInTheDocument()
      expect(screen.getByTestId('file-list-keywords')).toHaveTextContent('search-term')
      expect(screen.getByTestId('file-list-breadcrumbs')).toHaveTextContent('folder1/folder2')
      expect(screen.getByTestId('file-list-bucket')).toHaveTextContent('my-bucket')
      expect(screen.getByTestId('file-list-selected-count')).toHaveTextContent('2')
    })

    it('should pass docLink with correct path to Header', () => {
      // Arrange
      const props = createDefaultProps()

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

      // Assert
      expect(mockDocLink).toHaveBeenCalledWith('/guides/knowledge-base/knowledge-pipeline/authorize-data-source')
    })
  })

  // ==========================================
  // Props Testing
  // ==========================================
  describe('Props', () => {
    describe('nodeId prop', () => {
      it('should use nodeId in datasourceNodeRunURL for non-pipeline mode', async () => {
        // Arrange
        mockStoreState.currentCredentialId = 'cred-1'
        const props = createDefaultProps({
          nodeId: 'custom-node-id',
          isInPipeline: false,
        })

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

        // Assert - ssePost should be called with correct URL
        await waitFor(() => {
          expect(mockSsePost).toHaveBeenCalledWith(
            expect.stringContaining('/rag/pipelines/pipeline-123/workflows/published/datasource/nodes/custom-node-id/run'),
            expect.any(Object),
            expect.any(Object),
          )
        })
      })

      it('should use nodeId in datasourceNodeRunURL for pipeline mode', async () => {
        // Arrange
        mockStoreState.currentCredentialId = 'cred-1'
        const props = createDefaultProps({
          nodeId: 'custom-node-id',
          isInPipeline: true,
        })

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

        // Assert - ssePost should be called with correct URL for draft
        await waitFor(() => {
          expect(mockSsePost).toHaveBeenCalledWith(
            expect.stringContaining('/rag/pipelines/pipeline-123/workflows/draft/datasource/nodes/custom-node-id/run'),
            expect.any(Object),
            expect.any(Object),
          )
        })
      })
    })

    describe('nodeData prop', () => {
      it('should pass plugin_id and provider_name to useGetDataSourceAuth', () => {
        // Arrange
        const nodeData = createMockNodeData({
          plugin_id: 'my-plugin-id',
          provider_name: 'my-provider',
        })
        const props = createDefaultProps({ nodeData })

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

        // Assert
        expect(mockUseGetDataSourceAuth).toHaveBeenCalledWith({
          pluginId: 'my-plugin-id',
          provider: 'my-provider',
        })
      })

      it('should pass datasource_label to Header as pluginName', () => {
        // Arrange
        const nodeData = createMockNodeData({
          datasource_label: 'Custom Online Drive',
        })
        const props = createDefaultProps({ nodeData })

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

        // Assert
        expect(screen.getByTestId('header-plugin-name')).toHaveTextContent('Custom Online Drive')
      })
    })

    describe('isInPipeline prop', () => {
      it('should use draft URL when isInPipeline is true', async () => {
        // Arrange
        mockStoreState.currentCredentialId = 'cred-1'
        const props = createDefaultProps({ isInPipeline: true })

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

        // Assert
        await waitFor(() => {
          expect(mockSsePost).toHaveBeenCalledWith(
            expect.stringContaining('/workflows/draft/'),
            expect.any(Object),
            expect.any(Object),
          )
        })
      })

      it('should use published URL when isInPipeline is false', async () => {
        // Arrange
        mockStoreState.currentCredentialId = 'cred-1'
        const props = createDefaultProps({ isInPipeline: false })

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

        // Assert
        await waitFor(() => {
          expect(mockSsePost).toHaveBeenCalledWith(
            expect.stringContaining('/workflows/published/'),
            expect.any(Object),
            expect.any(Object),
          )
        })
      })

      it('should pass isInPipeline to FileList', () => {
        // Arrange
        const props = createDefaultProps({ isInPipeline: true })

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

        // Assert
        expect(screen.getByTestId('file-list-is-in-pipeline')).toHaveTextContent('true')
      })
    })

    describe('supportBatchUpload prop', () => {
      it('should pass supportBatchUpload true to FileList when supportBatchUpload is true', () => {
        // Arrange
        const props = createDefaultProps({ supportBatchUpload: true })

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

        // Assert
        expect(screen.getByTestId('file-list-support-batch')).toHaveTextContent('true')
      })

      it('should pass supportBatchUpload false to FileList when supportBatchUpload is false', () => {
        // Arrange
        const props = createDefaultProps({ supportBatchUpload: false })

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

        // Assert
        expect(screen.getByTestId('file-list-support-batch')).toHaveTextContent('false')
      })

      it.each([
        [true, 'true'],
        [false, 'false'],
        [undefined, 'true'], // Default value
      ])('should handle supportBatchUpload=%s correctly', (value, expected) => {
        // Arrange
        const props = createDefaultProps({ supportBatchUpload: value })

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

        // Assert
        expect(screen.getByTestId('file-list-support-batch')).toHaveTextContent(expected)
      })
    })

    describe('onCredentialChange prop', () => {
      it('should call onCredentialChange with credential id', () => {
        // Arrange
        const mockOnCredentialChange = jest.fn()
        const props = createDefaultProps({ onCredentialChange: mockOnCredentialChange })

        // Act
        render(<OnlineDrive {...props} />)
        fireEvent.click(screen.getByTestId('header-credential-change'))

        // Assert
        expect(mockOnCredentialChange).toHaveBeenCalledWith('new-cred-id')
      })
    })
  })

  // ==========================================
  // State Management Tests
  // ==========================================
  describe('State Management', () => {
    it('should fetch files on initial mount when fileList is empty', async () => {
      // Arrange
      mockStoreState.currentCredentialId = 'cred-1'
      mockStoreState.onlineDriveFileList = []
      const props = createDefaultProps()

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

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

    it('should not fetch files on initial mount when fileList is not empty', async () => {
      // Arrange
      mockStoreState.currentCredentialId = 'cred-1'
      mockStoreState.onlineDriveFileList = [createMockOnlineDriveFile()]
      const props = createDefaultProps()

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

      // Assert - Wait a bit to ensure no call is made
      await new Promise(resolve => setTimeout(resolve, 100))
      expect(mockSsePost).not.toHaveBeenCalled()
    })

    it('should not fetch files when currentCredentialId is empty', async () => {
      // Arrange
      mockStoreState.currentCredentialId = ''
      const props = createDefaultProps()

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

      // Assert - Wait a bit to ensure no call is made
      await new Promise(resolve => setTimeout(resolve, 100))
      expect(mockSsePost).not.toHaveBeenCalled()
    })

    it('should show loading state during fetch', async () => {
      // Arrange
      mockStoreState.currentCredentialId = 'cred-1'
      mockSsePost.mockImplementation(() => {
        // Never resolves to keep loading state
      })
      const props = createDefaultProps()

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

      // Assert
      await waitFor(() => {
        expect(screen.getByTestId('file-list-loading')).toHaveTextContent('true')
      })
    })

    it('should update file list on successful fetch', async () => {
      // Arrange
      mockStoreState.currentCredentialId = 'cred-1'
      const mockFiles = [
        { id: 'file-1', name: 'file1.txt', type: 'file' as const },
        { id: 'file-2', name: 'file2.txt', type: 'file' as const },
      ]
      mockSsePost.mockImplementation((url, options, callbacks) => {
        callbacks.onDataSourceNodeCompleted({
          data: [{
            bucket: '',
            files: mockFiles,
            is_truncated: false,
            next_page_parameters: {},
          }],
          time_consuming: 1.0,
        })
      })
      const props = createDefaultProps()

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

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

    it('should show error toast on fetch error', async () => {
      // Arrange
      mockStoreState.currentCredentialId = 'cred-1'
      const errorMessage = 'Failed to fetch files'
      mockSsePost.mockImplementation((url, options, callbacks) => {
        callbacks.onDataSourceNodeError({
          error: errorMessage,
        })
      })
      const props = createDefaultProps()

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

      // Assert
      await waitFor(() => {
        expect(mockToastNotify).toHaveBeenCalledWith({
          type: 'error',
          message: errorMessage,
        })
      })
    })
  })

  // ==========================================
  // Memoization Logic and Dependencies Tests
  // ==========================================
  describe('Memoization Logic', () => {
    it('should filter files by keywords', () => {
      // Arrange
      mockStoreState.keywords = 'test'
      mockStoreState.onlineDriveFileList = [
        createMockOnlineDriveFile({ id: '1', name: 'test-file.txt' }),
        createMockOnlineDriveFile({ id: '2', name: 'other-file.txt' }),
        createMockOnlineDriveFile({ id: '3', name: 'another-test.pdf' }),
      ]
      const props = createDefaultProps()

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

      // Assert - filteredOnlineDriveFileList should have 2 items matching 'test'
      expect(screen.getByTestId('file-list-count')).toHaveTextContent('2')
    })

    it('should return all files when keywords is empty', () => {
      // Arrange
      mockStoreState.keywords = ''
      mockStoreState.onlineDriveFileList = [
        createMockOnlineDriveFile({ id: '1', name: 'file1.txt' }),
        createMockOnlineDriveFile({ id: '2', name: 'file2.txt' }),
        createMockOnlineDriveFile({ id: '3', name: 'file3.pdf' }),
      ]
      const props = createDefaultProps()

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

      // Assert
      expect(screen.getByTestId('file-list-count')).toHaveTextContent('3')
    })

    it('should filter files case-insensitively', () => {
      // Arrange
      mockStoreState.keywords = 'TEST'
      mockStoreState.onlineDriveFileList = [
        createMockOnlineDriveFile({ id: '1', name: 'test-file.txt' }),
        createMockOnlineDriveFile({ id: '2', name: 'Test-Document.pdf' }),
        createMockOnlineDriveFile({ id: '3', name: 'other.txt' }),
      ]
      const props = createDefaultProps()

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

      // Assert
      expect(screen.getByTestId('file-list-count')).toHaveTextContent('2')
    })
  })

  // ==========================================
  // Callback Stability and Memoization
  // ==========================================
  describe('Callback Stability and Memoization', () => {
    it('should have stable handleSetting callback', () => {
      // Arrange
      const props = createDefaultProps()
      render(<OnlineDrive {...props} />)

      // Act
      fireEvent.click(screen.getByTestId('header-config-btn'))

      // Assert
      expect(mockSetShowAccountSettingModal).toHaveBeenCalledWith({
        payload: ACCOUNT_SETTING_TAB.DATA_SOURCE,
      })
    })

    it('should have stable updateKeywords that updates store', () => {
      // Arrange
      const props = createDefaultProps()
      render(<OnlineDrive {...props} />)

      // Act
      fireEvent.change(screen.getByTestId('file-list-search-input'), { target: { value: 'new-keyword' } })

      // Assert
      expect(mockStoreState.setKeywords).toHaveBeenCalledWith('new-keyword')
    })

    it('should have stable resetKeywords that clears keywords', () => {
      // Arrange
      mockStoreState.keywords = 'old-keyword'
      const props = createDefaultProps()
      render(<OnlineDrive {...props} />)

      // Act
      fireEvent.click(screen.getByTestId('file-list-reset-keywords'))

      // Assert
      expect(mockStoreState.setKeywords).toHaveBeenCalledWith('')
    })
  })

  // ==========================================
  // User Interactions and Event Handlers
  // ==========================================
  describe('User Interactions', () => {
    describe('File Selection', () => {
      it('should toggle file selection on file click', () => {
        // Arrange
        mockStoreState.selectedFileIds = []
        const props = createDefaultProps()
        render(<OnlineDrive {...props} />)

        // Act
        fireEvent.click(screen.getByTestId('file-list-select-file'))

        // Assert
        expect(mockStoreState.setSelectedFileIds).toHaveBeenCalledWith(['file-1'])
      })

      it('should deselect file if already selected', () => {
        // Arrange
        mockStoreState.selectedFileIds = ['file-1']
        const props = createDefaultProps()
        render(<OnlineDrive {...props} />)

        // Act
        fireEvent.click(screen.getByTestId('file-list-select-file'))

        // Assert
        expect(mockStoreState.setSelectedFileIds).toHaveBeenCalledWith([])
      })

      it('should not select bucket type items', () => {
        // Arrange
        mockStoreState.selectedFileIds = []
        const props = createDefaultProps()
        render(<OnlineDrive {...props} />)

        // Act
        fireEvent.click(screen.getByTestId('file-list-select-bucket'))

        // Assert
        expect(mockStoreState.setSelectedFileIds).not.toHaveBeenCalled()
      })

      it('should limit selection to one file when supportBatchUpload is false', () => {
        // Arrange
        mockStoreState.selectedFileIds = ['existing-file']
        const props = createDefaultProps({ supportBatchUpload: false })
        render(<OnlineDrive {...props} />)

        // Act
        fireEvent.click(screen.getByTestId('file-list-select-file'))

        // Assert - Should not add new file because there's already one selected
        expect(mockStoreState.setSelectedFileIds).toHaveBeenCalledWith(['existing-file'])
      })

      it('should allow multiple selections when supportBatchUpload is true', () => {
        // Arrange
        mockStoreState.selectedFileIds = ['existing-file']
        const props = createDefaultProps({ supportBatchUpload: true })
        render(<OnlineDrive {...props} />)

        // Act
        fireEvent.click(screen.getByTestId('file-list-select-file'))

        // Assert
        expect(mockStoreState.setSelectedFileIds).toHaveBeenCalledWith(['existing-file', 'file-1'])
      })
    })

    describe('Folder Navigation', () => {
      it('should open folder and update breadcrumbs/prefix', () => {
        // Arrange
        mockStoreState.breadcrumbs = []
        mockStoreState.prefix = []
        const props = createDefaultProps()
        render(<OnlineDrive {...props} />)

        // Act
        fireEvent.click(screen.getByTestId('file-list-open-folder'))

        // Assert
        expect(mockStoreState.setOnlineDriveFileList).toHaveBeenCalledWith([])
        expect(mockStoreState.setSelectedFileIds).toHaveBeenCalledWith([])
        expect(mockStoreState.setBreadcrumbs).toHaveBeenCalledWith(['my-folder'])
        expect(mockStoreState.setPrefix).toHaveBeenCalledWith(['folder-1'])
      })

      it('should open bucket and set bucket name', () => {
        // Arrange
        const props = createDefaultProps()
        render(<OnlineDrive {...props} />)

        // Act
        fireEvent.click(screen.getByTestId('file-list-open-bucket'))

        // Assert
        expect(mockStoreState.setOnlineDriveFileList).toHaveBeenCalledWith([])
        expect(mockStoreState.setBucket).toHaveBeenCalledWith('my-bucket')
      })

      it('should not navigate when opening a file', () => {
        // Arrange
        const props = createDefaultProps()
        render(<OnlineDrive {...props} />)

        // Act
        fireEvent.click(screen.getByTestId('file-list-open-file'))

        // Assert - No navigation functions should be called
        expect(mockStoreState.setBreadcrumbs).not.toHaveBeenCalled()
        expect(mockStoreState.setPrefix).not.toHaveBeenCalled()
        expect(mockStoreState.setBucket).not.toHaveBeenCalled()
      })
    })

    describe('Credential Change', () => {
      it('should call onCredentialChange prop', () => {
        // Arrange
        const mockOnCredentialChange = jest.fn()
        const props = createDefaultProps({ onCredentialChange: mockOnCredentialChange })
        render(<OnlineDrive {...props} />)

        // Act
        fireEvent.click(screen.getByTestId('header-credential-change'))

        // Assert
        expect(mockOnCredentialChange).toHaveBeenCalledWith('new-cred-id')
      })
    })

    describe('Configuration', () => {
      it('should open account setting modal on configuration click', () => {
        // Arrange
        const props = createDefaultProps()
        render(<OnlineDrive {...props} />)

        // Act
        fireEvent.click(screen.getByTestId('header-config-btn'))

        // Assert
        expect(mockSetShowAccountSettingModal).toHaveBeenCalledWith({
          payload: ACCOUNT_SETTING_TAB.DATA_SOURCE,
        })
      })
    })
  })

  // ==========================================
  // Side Effects and Cleanup Tests
  // ==========================================
  describe('Side Effects and Cleanup', () => {
    it('should fetch files when nextPageParameters changes after initial mount', async () => {
      // Arrange
      mockStoreState.currentCredentialId = 'cred-1'
      mockStoreState.onlineDriveFileList = [createMockOnlineDriveFile()]
      const props = createDefaultProps()
      const { rerender } = render(<OnlineDrive {...props} />)

      // Act - Simulate nextPageParameters change by re-rendering with updated state
      mockStoreState.nextPageParameters = { page: 2 }
      rerender(<OnlineDrive {...props} />)

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

    it('should fetch files when prefix changes after initial mount', async () => {
      // Arrange
      mockStoreState.currentCredentialId = 'cred-1'
      mockStoreState.onlineDriveFileList = [createMockOnlineDriveFile()]
      const props = createDefaultProps()
      const { rerender } = render(<OnlineDrive {...props} />)

      // Act - Simulate prefix change by re-rendering with updated state
      mockStoreState.prefix = ['folder1']
      rerender(<OnlineDrive {...props} />)

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

    it('should fetch files when bucket changes after initial mount', async () => {
      // Arrange
      mockStoreState.currentCredentialId = 'cred-1'
      mockStoreState.onlineDriveFileList = [createMockOnlineDriveFile()]
      const props = createDefaultProps()
      const { rerender } = render(<OnlineDrive {...props} />)

      // Act - Simulate bucket change by re-rendering with updated state
      mockStoreState.bucket = 'new-bucket'
      rerender(<OnlineDrive {...props} />)

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

    it('should fetch files when currentCredentialId changes after initial mount', async () => {
      // Arrange
      mockStoreState.currentCredentialId = 'cred-1'
      mockStoreState.onlineDriveFileList = [createMockOnlineDriveFile()]
      const props = createDefaultProps()
      const { rerender } = render(<OnlineDrive {...props} />)

      // Act - Simulate credential change by re-rendering with updated state
      mockStoreState.currentCredentialId = 'cred-2'
      rerender(<OnlineDrive {...props} />)

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

    it('should not fetch files concurrently (debounce)', async () => {
      // Arrange
      mockStoreState.currentCredentialId = 'cred-1'
      let resolveFirst: () => void
      const firstPromise = new Promise<void>((resolve) => {
        resolveFirst = resolve
      })
      mockSsePost.mockImplementationOnce((url, options, callbacks) => {
        firstPromise.then(() => {
          callbacks.onDataSourceNodeCompleted({
            data: [{ bucket: '', files: [], is_truncated: false, next_page_parameters: {} }],
            time_consuming: 1.0,
          })
        })
      })
      const props = createDefaultProps()

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

      // Try to trigger another fetch while first is loading
      mockStoreState.prefix = ['folder1']

      // Assert - Only one call should be made initially due to isLoadingRef guard
      expect(mockSsePost).toHaveBeenCalledTimes(1)

      // Cleanup
      resolveFirst!()
    })
  })

  // ==========================================
  // API Calls Mocking Tests
  // ==========================================
  describe('API Calls', () => {
    it('should call ssePost with correct parameters', async () => {
      // Arrange
      mockStoreState.currentCredentialId = 'cred-1'
      mockStoreState.prefix = ['folder1']
      mockStoreState.bucket = 'my-bucket'
      mockStoreState.nextPageParameters = { cursor: 'abc' }
      const props = createDefaultProps()

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

      // Assert
      await waitFor(() => {
        expect(mockSsePost).toHaveBeenCalledWith(
          expect.any(String),
          {
            body: {
              inputs: {
                prefix: 'folder1',
                bucket: 'my-bucket',
                next_page_parameters: { cursor: 'abc' },
                max_keys: 30,
              },
              datasource_type: DatasourceType.onlineDrive,
              credential_id: 'cred-1',
            },
          },
          expect.objectContaining({
            onDataSourceNodeCompleted: expect.any(Function),
            onDataSourceNodeError: expect.any(Function),
          }),
        )
      })
    })

    it('should handle completed response and update store', async () => {
      // Arrange
      mockStoreState.currentCredentialId = 'cred-1'
      mockStoreState.breadcrumbs = ['folder1']
      mockStoreState.bucket = 'my-bucket'
      const mockResponseData = [{
        bucket: 'my-bucket',
        files: [
          { id: 'file-1', name: 'file1.txt', size: 1024, type: 'file' as const },
          { id: 'file-2', name: 'file2.txt', size: 2048, type: 'file' as const },
        ],
        is_truncated: true,
        next_page_parameters: { cursor: 'next-cursor' },
      }]
      mockSsePost.mockImplementation((url, options, callbacks) => {
        callbacks.onDataSourceNodeCompleted({
          data: mockResponseData,
          time_consuming: 1.5,
        })
      })
      const props = createDefaultProps()

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

      // Assert
      await waitFor(() => {
        expect(mockStoreState.setOnlineDriveFileList).toHaveBeenCalled()
        expect(mockStoreState.setHasBucket).toHaveBeenCalledWith(true)
        expect(mockStoreState.isTruncated.current).toBe(true)
        expect(mockStoreState.currentNextPageParametersRef.current).toEqual({ cursor: 'next-cursor' })
      })
    })

    it('should handle error response and show toast', async () => {
      // Arrange
      mockStoreState.currentCredentialId = 'cred-1'
      const errorMessage = 'Access denied'
      mockSsePost.mockImplementation((url, options, callbacks) => {
        callbacks.onDataSourceNodeError({
          error: errorMessage,
        })
      })
      const props = createDefaultProps()

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

      // Assert
      await waitFor(() => {
        expect(mockToastNotify).toHaveBeenCalledWith({
          type: 'error',
          message: errorMessage,
        })
      })
    })
  })

  // ==========================================
  // Edge Cases and Error Handling
  // ==========================================
  describe('Edge Cases and Error Handling', () => {
    it('should handle empty credentials list', () => {
      // Arrange
      mockUseGetDataSourceAuth.mockReturnValue({
        data: { result: [] },
      })
      const props = createDefaultProps()

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

      // Assert
      expect(screen.getByTestId('header-credentials-count')).toHaveTextContent('0')
    })

    it('should handle undefined credentials data', () => {
      // Arrange
      mockUseGetDataSourceAuth.mockReturnValue({
        data: undefined,
      })
      const props = createDefaultProps()

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

      // Assert
      expect(screen.getByTestId('header-credentials-count')).toHaveTextContent('0')
    })

    it('should handle undefined pipelineId', async () => {
      // Arrange
      mockPipelineId = undefined
      mockStoreState.currentCredentialId = 'cred-1'
      const props = createDefaultProps()

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

      // Assert - Should still attempt to call ssePost with undefined in URL
      await waitFor(() => {
        expect(mockSsePost).toHaveBeenCalledWith(
          expect.stringContaining('/rag/pipelines/undefined/'),
          expect.any(Object),
          expect.any(Object),
        )
      })
    })

    it('should handle empty file list', () => {
      // Arrange
      mockStoreState.onlineDriveFileList = []
      const props = createDefaultProps()

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

      // Assert
      expect(screen.getByTestId('file-list-count')).toHaveTextContent('0')
    })

    it('should handle empty breadcrumbs', () => {
      // Arrange
      mockStoreState.breadcrumbs = []
      const props = createDefaultProps()

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

      // Assert
      expect(screen.getByTestId('file-list-breadcrumbs')).toHaveTextContent('')
    })

    it('should handle empty bucket', () => {
      // Arrange
      mockStoreState.bucket = ''
      const props = createDefaultProps()

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

      // Assert
      expect(screen.getByTestId('file-list-bucket')).toHaveTextContent('')
    })

    it('should handle special characters in keywords', () => {
      // Arrange
      mockStoreState.keywords = 'test.file[1]'
      mockStoreState.onlineDriveFileList = [
        createMockOnlineDriveFile({ id: '1', name: 'test.file[1].txt' }),
        createMockOnlineDriveFile({ id: '2', name: 'other.txt' }),
      ]
      const props = createDefaultProps()

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

      // Assert - Should find file with special characters
      expect(screen.getByTestId('file-list-count')).toHaveTextContent('1')
    })

    it('should handle very long file names', () => {
      // Arrange
      const longName = `${'a'.repeat(500)}.txt`
      mockStoreState.onlineDriveFileList = [
        createMockOnlineDriveFile({ id: '1', name: longName }),
      ]
      const props = createDefaultProps()

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

      // Assert
      expect(screen.getByTestId('file-list-count')).toHaveTextContent('1')
    })

    it('should handle bucket list initiation response', async () => {
      // Arrange
      mockStoreState.currentCredentialId = 'cred-1'
      mockStoreState.bucket = ''
      mockStoreState.prefix = []
      const mockBucketResponse = [
        { bucket: 'bucket-1', files: [], is_truncated: false, next_page_parameters: {} },
        { bucket: 'bucket-2', files: [], is_truncated: false, next_page_parameters: {} },
      ]
      mockSsePost.mockImplementation((url, options, callbacks) => {
        callbacks.onDataSourceNodeCompleted({
          data: mockBucketResponse,
          time_consuming: 1.0,
        })
      })
      const props = createDefaultProps()

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

      // Assert
      await waitFor(() => {
        expect(mockStoreState.setHasBucket).toHaveBeenCalledWith(true)
      })
    })
  })

  // ==========================================
  // All Prop Variations Tests
  // ==========================================
  describe('Prop Variations', () => {
    it.each([
      { isInPipeline: true, supportBatchUpload: true },
      { isInPipeline: true, supportBatchUpload: false },
      { isInPipeline: false, supportBatchUpload: true },
      { isInPipeline: false, supportBatchUpload: false },
    ])('should render correctly with isInPipeline=%s and supportBatchUpload=%s', (propVariation) => {
      // Arrange
      const props = createDefaultProps(propVariation)

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

      // Assert
      expect(screen.getByTestId('header')).toBeInTheDocument()
      expect(screen.getByTestId('file-list')).toBeInTheDocument()
      expect(screen.getByTestId('file-list-is-in-pipeline')).toHaveTextContent(String(propVariation.isInPipeline))
      expect(screen.getByTestId('file-list-support-batch')).toHaveTextContent(String(propVariation.supportBatchUpload))
    })

    it.each([
      { nodeId: 'node-a', expectedUrlPart: 'nodes/node-a/run' },
      { nodeId: 'node-b', expectedUrlPart: 'nodes/node-b/run' },
      { nodeId: '123-456', expectedUrlPart: 'nodes/123-456/run' },
    ])('should use correct URL for nodeId=%s', async ({ nodeId, expectedUrlPart }) => {
      // Arrange
      mockStoreState.currentCredentialId = 'cred-1'
      const props = createDefaultProps({ nodeId })

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

      // Assert
      await waitFor(() => {
        expect(mockSsePost).toHaveBeenCalledWith(
          expect.stringContaining(expectedUrlPart),
          expect.any(Object),
          expect.any(Object),
        )
      })
    })

    it.each([
      { pluginId: 'plugin-a', providerName: 'provider-a' },
      { pluginId: 'plugin-b', providerName: 'provider-b' },
      { pluginId: '', providerName: '' },
    ])('should call useGetDataSourceAuth with pluginId=%s and providerName=%s', ({ pluginId, providerName }) => {
      // Arrange
      const props = createDefaultProps({
        nodeData: createMockNodeData({
          plugin_id: pluginId,
          provider_name: providerName,
        }),
      })

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

      // Assert
      expect(mockUseGetDataSourceAuth).toHaveBeenCalledWith({
        pluginId,
        provider: providerName,
      })
    })
  })
})

// ==========================================
// Header Component Tests
// ==========================================
describe('Header', () => {
  const createHeaderProps = (overrides?: Partial<React.ComponentProps<typeof Header>>) => ({
    onClickConfiguration: jest.fn(),
    docTitle: 'Documentation',
    docLink: 'https://docs.example.com/guide',
    ...overrides,
  })

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

  describe('Rendering', () => {
    it('should render without crashing', () => {
      // Arrange
      const props = createHeaderProps()

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

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

    it('should render doc link with correct href', () => {
      // Arrange
      const props = createHeaderProps({
        docLink: 'https://custom-docs.com/path',
        docTitle: 'Custom Docs',
      })

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

      // Assert
      const link = screen.getByRole('link')
      expect(link).toHaveAttribute('href', 'https://custom-docs.com/path')
      expect(link).toHaveAttribute('target', '_blank')
      expect(link).toHaveAttribute('rel', 'noopener noreferrer')
    })

    it('should render doc title text', () => {
      // Arrange
      const props = createHeaderProps({ docTitle: 'My Documentation Title' })

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

      // Assert
      expect(screen.getByText('My Documentation Title')).toBeInTheDocument()
    })

    it('should render configuration button', () => {
      // Arrange
      const props = createHeaderProps()

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

      // Assert
      expect(screen.getByRole('button')).toBeInTheDocument()
    })
  })

  describe('Props', () => {
    describe('docTitle prop', () => {
      it.each([
        'Getting Started',
        'API Reference',
        'Installation Guide',
        '',
      ])('should render docTitle="%s"', (docTitle) => {
        // Arrange
        const props = createHeaderProps({ docTitle })

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

        // Assert
        if (docTitle)
          expect(screen.getByText(docTitle)).toBeInTheDocument()
      })
    })

    describe('docLink prop', () => {
      it.each([
        'https://docs.example.com',
        'https://docs.example.com/path/to/page',
        '/relative/path',
      ])('should set href to "%s"', (docLink) => {
        // Arrange
        const props = createHeaderProps({ docLink })

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

        // Assert
        expect(screen.getByRole('link')).toHaveAttribute('href', docLink)
      })
    })

    describe('onClickConfiguration prop', () => {
      it('should call onClickConfiguration when configuration icon is clicked', () => {
        // Arrange
        const mockOnClickConfiguration = jest.fn()
        const props = createHeaderProps({ onClickConfiguration: mockOnClickConfiguration })

        // Act
        render(<Header {...props} />)
        const configIcon = screen.getByRole('button').querySelector('svg')
        fireEvent.click(configIcon!)

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

      it('should not throw when onClickConfiguration is undefined', () => {
        // Arrange
        const props = createHeaderProps({ onClickConfiguration: undefined })

        // Act & Assert
        expect(() => render(<Header {...props} />)).not.toThrow()
      })
    })
  })

  describe('Accessibility', () => {
    it('should have accessible link with title attribute', () => {
      // Arrange
      const props = createHeaderProps({ docTitle: 'Accessible Title' })

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

      // Assert
      const titleSpan = screen.getByTitle('Accessible Title')
      expect(titleSpan).toBeInTheDocument()
    })
  })
})

// ==========================================
// Utils Tests
// ==========================================
describe('utils', () => {
  // ==========================================
  // isFile Tests
  // ==========================================
  describe('isFile', () => {
    it('should return true for file type', () => {
      // Act & Assert
      expect(isFile('file')).toBe(true)
    })

    it('should return false for folder type', () => {
      // Act & Assert
      expect(isFile('folder')).toBe(false)
    })

    it.each([
      ['file', true],
      ['folder', false],
    ] as const)('isFile(%s) should return %s', (type, expected) => {
      // Act & Assert
      expect(isFile(type)).toBe(expected)
    })
  })

  // ==========================================
  // isBucketListInitiation Tests
  // ==========================================
  describe('isBucketListInitiation', () => {
    it('should return false when bucket is not empty', () => {
      // Arrange
      const data: OnlineDriveData[] = [
        { bucket: 'my-bucket', files: [], is_truncated: false, next_page_parameters: {} },
      ]

      // Act & Assert
      expect(isBucketListInitiation(data, [], 'existing-bucket')).toBe(false)
    })

    it('should return false when prefix is not empty', () => {
      // Arrange
      const data: OnlineDriveData[] = [
        { bucket: 'my-bucket', files: [], is_truncated: false, next_page_parameters: {} },
      ]

      // Act & Assert
      expect(isBucketListInitiation(data, ['folder1'], '')).toBe(false)
    })

    it('should return false when data items have no bucket', () => {
      // Arrange
      const data: OnlineDriveData[] = [
        { bucket: '', files: [{ id: '1', name: 'file.txt', size: 1024, type: 'file' }], is_truncated: false, next_page_parameters: {} },
      ]

      // Act & Assert
      expect(isBucketListInitiation(data, [], '')).toBe(false)
    })

    it('should return true for multiple buckets with no prefix and bucket', () => {
      // Arrange
      const data: OnlineDriveData[] = [
        { bucket: 'bucket-1', files: [], is_truncated: false, next_page_parameters: {} },
        { bucket: 'bucket-2', files: [], is_truncated: false, next_page_parameters: {} },
      ]

      // Act & Assert
      expect(isBucketListInitiation(data, [], '')).toBe(true)
    })

    it('should return true for single bucket with no files, no prefix, and no bucket', () => {
      // Arrange
      const data: OnlineDriveData[] = [
        { bucket: 'my-bucket', files: [], is_truncated: false, next_page_parameters: {} },
      ]

      // Act & Assert
      expect(isBucketListInitiation(data, [], '')).toBe(true)
    })

    it('should return false for single bucket with files', () => {
      // Arrange
      const data: OnlineDriveData[] = [
        { bucket: 'my-bucket', files: [{ id: '1', name: 'file.txt', size: 1024, type: 'file' }], is_truncated: false, next_page_parameters: {} },
      ]

      // Act & Assert
      expect(isBucketListInitiation(data, [], '')).toBe(false)
    })

    it('should return false for empty data array', () => {
      // Arrange
      const data: OnlineDriveData[] = []

      // Act & Assert
      expect(isBucketListInitiation(data, [], '')).toBe(false)
    })
  })

  // ==========================================
  // convertOnlineDriveData Tests
  // ==========================================
  describe('convertOnlineDriveData', () => {
    describe('Empty data handling', () => {
      it('should return empty result for empty data array', () => {
        // Arrange
        const data: OnlineDriveData[] = []

        // Act
        const result = convertOnlineDriveData(data, [], '')

        // Assert
        expect(result).toEqual({
          fileList: [],
          isTruncated: false,
          nextPageParameters: {},
          hasBucket: false,
        })
      })
    })

    describe('Bucket list initiation', () => {
      it('should convert multiple buckets to bucket file list', () => {
        // Arrange
        const data: OnlineDriveData[] = [
          { bucket: 'bucket-1', files: [], is_truncated: false, next_page_parameters: {} },
          { bucket: 'bucket-2', files: [], is_truncated: false, next_page_parameters: {} },
          { bucket: 'bucket-3', files: [], is_truncated: false, next_page_parameters: {} },
        ]

        // Act
        const result = convertOnlineDriveData(data, [], '')

        // Assert
        expect(result.fileList).toHaveLength(3)
        expect(result.fileList[0]).toEqual({
          id: 'bucket-1',
          name: 'bucket-1',
          type: OnlineDriveFileType.bucket,
        })
        expect(result.fileList[1]).toEqual({
          id: 'bucket-2',
          name: 'bucket-2',
          type: OnlineDriveFileType.bucket,
        })
        expect(result.fileList[2]).toEqual({
          id: 'bucket-3',
          name: 'bucket-3',
          type: OnlineDriveFileType.bucket,
        })
        expect(result.hasBucket).toBe(true)
        expect(result.isTruncated).toBe(false)
        expect(result.nextPageParameters).toEqual({})
      })

      it('should convert single bucket with no files to bucket list', () => {
        // Arrange
        const data: OnlineDriveData[] = [
          { bucket: 'my-bucket', files: [], is_truncated: false, next_page_parameters: {} },
        ]

        // Act
        const result = convertOnlineDriveData(data, [], '')

        // Assert
        expect(result.fileList).toHaveLength(1)
        expect(result.fileList[0]).toEqual({
          id: 'my-bucket',
          name: 'my-bucket',
          type: OnlineDriveFileType.bucket,
        })
        expect(result.hasBucket).toBe(true)
      })
    })

    describe('File list conversion', () => {
      it('should convert files correctly', () => {
        // Arrange
        const data: OnlineDriveData[] = [
          {
            bucket: 'my-bucket',
            files: [
              { id: 'file-1', name: 'document.pdf', size: 1024, type: 'file' },
              { id: 'file-2', name: 'image.png', size: 2048, type: 'file' },
            ],
            is_truncated: false,
            next_page_parameters: {},
          },
        ]

        // Act
        const result = convertOnlineDriveData(data, ['folder1'], 'my-bucket')

        // Assert
        expect(result.fileList).toHaveLength(2)
        expect(result.fileList[0]).toEqual({
          id: 'file-1',
          name: 'document.pdf',
          size: 1024,
          type: OnlineDriveFileType.file,
        })
        expect(result.fileList[1]).toEqual({
          id: 'file-2',
          name: 'image.png',
          size: 2048,
          type: OnlineDriveFileType.file,
        })
        expect(result.hasBucket).toBe(true)
      })

      it('should convert folders correctly without size', () => {
        // Arrange
        const data: OnlineDriveData[] = [
          {
            bucket: 'my-bucket',
            files: [
              { id: 'folder-1', name: 'Documents', size: 0, type: 'folder' },
              { id: 'folder-2', name: 'Images', size: 0, type: 'folder' },
            ],
            is_truncated: false,
            next_page_parameters: {},
          },
        ]

        // Act
        const result = convertOnlineDriveData(data, [], 'my-bucket')

        // Assert
        expect(result.fileList).toHaveLength(2)
        expect(result.fileList[0]).toEqual({
          id: 'folder-1',
          name: 'Documents',
          size: undefined,
          type: OnlineDriveFileType.folder,
        })
        expect(result.fileList[1]).toEqual({
          id: 'folder-2',
          name: 'Images',
          size: undefined,
          type: OnlineDriveFileType.folder,
        })
      })

      it('should handle mixed files and folders', () => {
        // Arrange
        const data: OnlineDriveData[] = [
          {
            bucket: 'my-bucket',
            files: [
              { id: 'folder-1', name: 'Documents', size: 0, type: 'folder' },
              { id: 'file-1', name: 'readme.txt', size: 256, type: 'file' },
              { id: 'folder-2', name: 'Images', size: 0, type: 'folder' },
              { id: 'file-2', name: 'data.json', size: 512, type: 'file' },
            ],
            is_truncated: false,
            next_page_parameters: {},
          },
        ]

        // Act
        const result = convertOnlineDriveData(data, [], 'my-bucket')

        // Assert
        expect(result.fileList).toHaveLength(4)
        expect(result.fileList[0].type).toBe(OnlineDriveFileType.folder)
        expect(result.fileList[1].type).toBe(OnlineDriveFileType.file)
        expect(result.fileList[2].type).toBe(OnlineDriveFileType.folder)
        expect(result.fileList[3].type).toBe(OnlineDriveFileType.file)
      })
    })

    describe('Truncation and pagination', () => {
      it('should return isTruncated true when data is truncated', () => {
        // Arrange
        const data: OnlineDriveData[] = [
          {
            bucket: 'my-bucket',
            files: [{ id: 'file-1', name: 'file.txt', size: 1024, type: 'file' }],
            is_truncated: true,
            next_page_parameters: { cursor: 'next-cursor' },
          },
        ]

        // Act
        const result = convertOnlineDriveData(data, [], 'my-bucket')

        // Assert
        expect(result.isTruncated).toBe(true)
        expect(result.nextPageParameters).toEqual({ cursor: 'next-cursor' })
      })

      it('should return isTruncated false when not truncated', () => {
        // Arrange
        const data: OnlineDriveData[] = [
          {
            bucket: 'my-bucket',
            files: [{ id: 'file-1', name: 'file.txt', size: 1024, type: 'file' }],
            is_truncated: false,
            next_page_parameters: {},
          },
        ]

        // Act
        const result = convertOnlineDriveData(data, [], 'my-bucket')

        // Assert
        expect(result.isTruncated).toBe(false)
        expect(result.nextPageParameters).toEqual({})
      })

      it('should handle undefined is_truncated', () => {
        // Arrange
        const data: OnlineDriveData[] = [
          {
            bucket: 'my-bucket',
            files: [{ id: 'file-1', name: 'file.txt', size: 1024, type: 'file' }],
            is_truncated: undefined as any,
            next_page_parameters: undefined as any,
          },
        ]

        // Act
        const result = convertOnlineDriveData(data, [], 'my-bucket')

        // Assert
        expect(result.isTruncated).toBe(false)
        expect(result.nextPageParameters).toEqual({})
      })
    })

    describe('hasBucket flag', () => {
      it('should return hasBucket true when bucket exists in data', () => {
        // Arrange
        const data: OnlineDriveData[] = [
          {
            bucket: 'my-bucket',
            files: [{ id: 'file-1', name: 'file.txt', size: 1024, type: 'file' }],
            is_truncated: false,
            next_page_parameters: {},
          },
        ]

        // Act
        const result = convertOnlineDriveData(data, [], 'my-bucket')

        // Assert
        expect(result.hasBucket).toBe(true)
      })

      it('should return hasBucket false when bucket is empty in data', () => {
        // Arrange
        const data: OnlineDriveData[] = [
          {
            bucket: '',
            files: [{ id: 'file-1', name: 'file.txt', size: 1024, type: 'file' }],
            is_truncated: false,
            next_page_parameters: {},
          },
        ]

        // Act
        const result = convertOnlineDriveData(data, [], '')

        // Assert
        expect(result.hasBucket).toBe(false)
      })
    })

    describe('Edge cases', () => {
      it('should handle files with zero size', () => {
        // Arrange
        const data: OnlineDriveData[] = [
          {
            bucket: 'my-bucket',
            files: [{ id: 'file-1', name: 'empty.txt', size: 0, type: 'file' }],
            is_truncated: false,
            next_page_parameters: {},
          },
        ]

        // Act
        const result = convertOnlineDriveData(data, [], 'my-bucket')

        // Assert
        expect(result.fileList[0].size).toBe(0)
      })

      it('should handle files with very large size', () => {
        // Arrange
        const largeSize = Number.MAX_SAFE_INTEGER
        const data: OnlineDriveData[] = [
          {
            bucket: 'my-bucket',
            files: [{ id: 'file-1', name: 'large.bin', size: largeSize, type: 'file' }],
            is_truncated: false,
            next_page_parameters: {},
          },
        ]

        // Act
        const result = convertOnlineDriveData(data, [], 'my-bucket')

        // Assert
        expect(result.fileList[0].size).toBe(largeSize)
      })

      it('should handle files with special characters in name', () => {
        // Arrange
        const data: OnlineDriveData[] = [
          {
            bucket: 'my-bucket',
            files: [
              { id: 'file-1', name: 'file[1] (copy).txt', size: 1024, type: 'file' },
              { id: 'file-2', name: 'doc-with-dash_and_underscore.pdf', size: 2048, type: 'file' },
              { id: 'file-3', name: 'file with spaces.txt', size: 512, type: 'file' },
            ],
            is_truncated: false,
            next_page_parameters: {},
          },
        ]

        // Act
        const result = convertOnlineDriveData(data, [], 'my-bucket')

        // Assert
        expect(result.fileList[0].name).toBe('file[1] (copy).txt')
        expect(result.fileList[1].name).toBe('doc-with-dash_and_underscore.pdf')
        expect(result.fileList[2].name).toBe('file with spaces.txt')
      })

      it('should handle complex next_page_parameters', () => {
        // Arrange
        const complexParams = {
          cursor: 'abc123',
          page: 2,
          limit: 50,
          nested: { key: 'value' },
        }
        const data: OnlineDriveData[] = [
          {
            bucket: 'my-bucket',
            files: [{ id: 'file-1', name: 'file.txt', size: 1024, type: 'file' }],
            is_truncated: true,
            next_page_parameters: complexParams,
          },
        ]

        // Act
        const result = convertOnlineDriveData(data, [], 'my-bucket')

        // Assert
        expect(result.nextPageParameters).toEqual(complexParams)
      })
    })
  })
})
