import type { ReactNode } from 'react'
import type { App } from '@/models/explore'
import type { TryAppSelection } from '@/types/try-app'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { fireEvent, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import * as React from 'react'
import { useContextSelector } from 'use-context-selector'
import AppListContext from '@/context/app-list-context'
import { fetchAppDetail } from '@/service/explore'
import { render } from '@/test/console/render'
import { AppModeEnum } from '@/types/app'
import Apps from '../index'

vi.mock('@/next/dynamic', () => ({
  default: (loader: () => Promise<{ default: React.ComponentType }>) => {
    const LazyComp = React.lazy(loader)
    return function DynamicWrapper(props: Record<string, unknown>) {
      return React.createElement(
        React.Suspense,
        { fallback: null },
        React.createElement(LazyComp, props),
      )
    }
  },
}))

let documentTitleCalls: string[] = []
let educationInitCalls: number = 0
const mockHandleImportDSL = vi.fn()
const mockHandleImportDSLConfirm = vi.fn()
const mockTrackCreateApp = vi.fn()
const mockFetchAppDetail = vi.mocked(fetchAppDetail)
let mockWorkspacePermissionKeys: string[] = ['app.create_and_management']

const mockTemplateApp: App = {
  app_id: 'template-1',
  categories: ['Assistant'],
  app: {
    id: 'template-1',
    mode: AppModeEnum.CHAT,
    icon_type: 'emoji',
    icon: '🤖',
    icon_background: '#fff',
    icon_url: '',
    name: 'Sample App',
    description: 'Sample App',
    use_icon_as_answer_icon: false,
  },
  description: 'Sample App',
  can_trial: true,
  copyright: '',
  privacy_policy: null,
  custom_disclaimer: null,
  position: 1,
  is_listed: true,
  install_count: 0,
  installed: false,
  editable: false,
  is_agent: false,
}

vi.mock('@/hooks/use-document-title', () => ({
  default: (title: string) => {
    documentTitleCalls.push(title)
  },
}))

vi.mock('@/app/education-apply/hooks', () => ({
  useEducationInit: () => {
    educationInitCalls++
  },
}))

vi.mock('@/context/permission-state', async () => {
  const { createPermissionStateModuleMock } = await import('@/test/console/state-fixture')
  return createPermissionStateModuleMock(() => ({
    workspacePermissionKeys: mockWorkspacePermissionKeys,
  }))
})

vi.mock('@/hooks/use-import-dsl', () => ({
  useImportDSL: () => ({
    handleImportDSL: mockHandleImportDSL,
    handleImportDSLConfirm: mockHandleImportDSLConfirm,
    versions: [],
    isFetching: false,
  }),
}))

const mockReplace = vi.fn()
let mockSearchParams = new URLSearchParams()

vi.mock('@/next/navigation', () => ({
  useRouter: () => ({
    replace: mockReplace,
  }),
  useSearchParams: () => mockSearchParams,
}))

vi.mock('../list', () => {
  const MockList = ({
    onCreateLearnDify,
    onTryLearnDify,
  }: {
    onCreateLearnDify?: (app: App) => void
    onTryLearnDify?: (params: TryAppSelection) => void
  }) => {
    const setShowTryAppPanel = useContextSelector(AppListContext, (ctx) => ctx.setShowTryAppPanel)
    return React.createElement(
      'div',
      { 'data-testid': 'apps-list' },
      React.createElement('span', null, 'Apps List'),
      React.createElement(
        'button',
        {
          'data-testid': 'open-preview',
          onClick: () =>
            setShowTryAppPanel(true, {
              appId: mockTemplateApp.app_id,
              app: mockTemplateApp,
            }),
        },
        'Open Preview',
      ),
      React.createElement(
        'button',
        {
          type: 'button',
          onClick: () => onTryLearnDify?.({ appId: mockTemplateApp.app_id, app: mockTemplateApp }),
        },
        'Preview Learn Dify template',
      ),
      React.createElement(
        'button',
        { type: 'button', onClick: () => onCreateLearnDify?.(mockTemplateApp) },
        'Create Learn Dify template',
      ),
    )
  }

  return { default: MockList }
})

vi.mock('../../explore/try-app', () => ({
  default: ({ onCreate, onClose }: { onCreate: () => void; onClose: () => void }) => (
    <div data-testid="try-app-panel">
      <button data-testid="try-app-create" onClick={onCreate}>
        Create
      </button>
      <button data-testid="try-app-close" onClick={onClose}>
        Close
      </button>
    </div>
  ),
}))

vi.mock('../../explore/create-app-modal', () => ({
  default: ({
    show,
    onConfirm,
    onHide,
  }: {
    show: boolean
    onConfirm: (payload: Record<string, string>) => Promise<void>
    onHide: () => void
  }) =>
    show ? (
      <div data-testid="create-app-modal">
        <button
          data-testid="confirm-create"
          onClick={() =>
            onConfirm({
              name: 'Created App',
              icon_type: 'emoji',
              icon: '🤖',
              icon_background: '#fff',
              description: 'created from preview',
            })
          }
        >
          Confirm
        </button>
        <button data-testid="hide-create" onClick={onHide}>
          Hide
        </button>
      </div>
    ) : null,
}))

vi.mock('../../app/create-from-dsl-modal/dsl-confirm-modal', () => ({
  default: ({ onConfirm, onCancel }: { onConfirm: () => void; onCancel: () => void }) => (
    <div data-testid="dsl-confirm-modal">
      <button data-testid="confirm-dsl" onClick={onConfirm}>
        Confirm DSL
      </button>
      <button data-testid="cancel-dsl" onClick={onCancel}>
        Cancel DSL
      </button>
    </div>
  ),
}))

vi.mock('../import-from-marketplace-template-modal', () => ({
  default: ({
    templateId,
    onClose,
    onConfirm,
  }: {
    templateId: string
    onClose: () => void
    onConfirm: (dsl: string) => void
  }) => (
    <div data-testid="marketplace-template-modal">
      <span data-testid="template-id">{templateId}</span>
      <button data-testid="close-template" onClick={onClose}>
        Close Template
      </button>
      <button data-testid="confirm-template" onClick={() => onConfirm('yaml-dsl-content')}>
        Confirm Template
      </button>
    </div>
  ),
}))

vi.mock('@/service/explore', () => ({
  fetchAppDetail: vi.fn(),
}))

vi.mock('@/utils/create-app-tracking', () => ({
  trackCreateApp: (...args: unknown[]) => mockTrackCreateApp(...args),
}))

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

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

  beforeEach(() => {
    vi.clearAllMocks()
    documentTitleCalls = []
    educationInitCalls = 0
    mockWorkspacePermissionKeys = ['app.create_and_management']
    mockSearchParams = new URLSearchParams()
    mockReplace.mockClear()
    mockFetchAppDetail.mockResolvedValue({
      id: 'template-1',
      name: 'Sample App',
      icon: '🤖',
      icon_background: '#fff',
      mode: AppModeEnum.CHAT,
      export_data: 'yaml-content',
    })
  })

  describe('Integration', () => {
    it('should track template preview creation after a successful import', async () => {
      mockHandleImportDSL.mockImplementation(
        async (
          _payload: unknown,
          options: { onSuccess?: (payload: { app_mode: AppModeEnum }) => void },
        ) => {
          options.onSuccess?.({ app_mode: AppModeEnum.CHAT })
        },
      )

      renderWithClient(<Apps />)

      fireEvent.click(screen.getByTestId('open-preview'))
      fireEvent.click(await screen.findByTestId('try-app-create'))
      fireEvent.click(await screen.findByTestId('confirm-create'))

      await waitFor(() => {
        expect(mockFetchAppDetail).toHaveBeenCalledWith('template-1')
        expect(mockTrackCreateApp).toHaveBeenCalledWith({
          source: 'studio_template_preview',
          appMode: AppModeEnum.CHAT,
          templateId: 'template-1',
        })
      })
    })

    it('should open the template preview from Learn Dify', async () => {
      const user = userEvent.setup()
      renderWithClient(<Apps />)

      await user.click(screen.getByRole('button', { name: 'Preview Learn Dify template' }))

      expect(await screen.findByTestId('try-app-panel')).toBeInTheDocument()
    })

    it('should open the create modal from Learn Dify', async () => {
      const user = userEvent.setup()
      renderWithClient(<Apps />)

      await user.click(screen.getByRole('button', { name: 'Create Learn Dify template' }))

      expect(await screen.findByTestId('create-app-modal')).toBeInTheDocument()
    })

    it('should track template preview creation after confirming a pending import', async () => {
      mockHandleImportDSL.mockImplementation(
        async (_payload: unknown, options: { onPending?: () => void }) => {
          options.onPending?.()
        },
      )
      mockHandleImportDSLConfirm.mockImplementation(
        async (options: { onSuccess?: (payload: { app_mode: AppModeEnum }) => void }) => {
          options.onSuccess?.({ app_mode: AppModeEnum.WORKFLOW })
        },
      )

      renderWithClient(<Apps />)

      fireEvent.click(screen.getByTestId('open-preview'))
      fireEvent.click(await screen.findByTestId('try-app-create'))
      fireEvent.click(await screen.findByTestId('confirm-create'))

      fireEvent.click(await screen.findByTestId('confirm-dsl'))

      await waitFor(() => {
        expect(mockHandleImportDSLConfirm).toHaveBeenCalledTimes(1)
        expect(mockTrackCreateApp).toHaveBeenCalledWith({
          source: 'studio_template_preview',
          appMode: AppModeEnum.WORKFLOW,
          templateId: 'template-1',
        })
      })
    })

    it('should close the dsl confirm modal when the pending import is canceled', async () => {
      mockHandleImportDSL.mockImplementation(
        async (_payload: unknown, options: { onPending?: () => void }) => {
          options.onPending?.()
        },
      )

      renderWithClient(<Apps />)

      fireEvent.click(screen.getByTestId('open-preview'))
      fireEvent.click(await screen.findByTestId('try-app-create'))
      fireEvent.click(await screen.findByTestId('confirm-create'))

      fireEvent.click(await screen.findByTestId('cancel-dsl'))

      await waitFor(() => {
        expect(screen.queryByTestId('dsl-confirm-modal')).not.toBeInTheDocument()
      })
      expect(mockTrackCreateApp).not.toHaveBeenCalled()
    })

    it('should hide the create modal without tracking when the modal closes', async () => {
      renderWithClient(<Apps />)

      fireEvent.click(screen.getByTestId('open-preview'))
      fireEvent.click(await screen.findByTestId('try-app-create'))

      fireEvent.click(await screen.findByTestId('hide-create'))

      await waitFor(() => {
        expect(screen.queryByTestId('create-app-modal')).not.toBeInTheDocument()
      })
      expect(mockTrackCreateApp).not.toHaveBeenCalled()
    })
  })

  describe('Marketplace Template', () => {
    it('should render the template modal when template-id is in search params', async () => {
      mockSearchParams = new URLSearchParams('template-id=tpl-42')
      renderWithClient(<Apps />)

      expect(await screen.findByTestId('marketplace-template-modal')).toBeInTheDocument()
      expect(screen.getByTestId('template-id')).toHaveTextContent('tpl-42')
    })

    it('should not render the template modal without app.create_and_management permission', () => {
      mockWorkspacePermissionKeys = []
      mockSearchParams = new URLSearchParams('template-id=tpl-42')
      renderWithClient(<Apps />)

      expect(screen.queryByTestId('marketplace-template-modal')).not.toBeInTheDocument()
    })

    it('should not render the template modal when no template-id is present', () => {
      renderWithClient(<Apps />)

      expect(screen.queryByTestId('marketplace-template-modal')).not.toBeInTheDocument()
    })

    it('should close the template modal and remove template-id from URL', async () => {
      mockSearchParams = new URLSearchParams('template-id=tpl-42')
      renderWithClient(<Apps />)

      fireEvent.click(await screen.findByTestId('close-template'))

      expect(mockReplace).toHaveBeenCalledTimes(1)
      const replaceArg = mockReplace.mock.calls[0]![0] as string
      expect(replaceArg).not.toContain('template-id')
    })

    it('should import DSL from marketplace template on confirm', async () => {
      mockHandleImportDSL.mockImplementation(
        async (
          _payload: unknown,
          options: { onSuccess?: (payload: { app_mode: AppModeEnum }) => void },
        ) => {
          options.onSuccess?.({ app_mode: AppModeEnum.CHAT })
        },
      )
      mockSearchParams = new URLSearchParams('template-id=tpl-42')
      renderWithClient(<Apps />)

      fireEvent.click(await screen.findByTestId('confirm-template'))

      await waitFor(() => {
        expect(mockHandleImportDSL).toHaveBeenCalledWith(
          { mode: 'yaml-content', yaml_content: 'yaml-dsl-content' },
          expect.objectContaining({ onSuccess: expect.any(Function) }),
        )
        expect(mockTrackCreateApp).toHaveBeenCalledWith({
          source: 'external',
          appMode: AppModeEnum.CHAT,
          templateId: 'tpl-42',
        })
        expect(mockReplace).toHaveBeenCalled()
      })
    })

    it('should not open create modal from template preview without app.create_and_management permission', async () => {
      mockWorkspacePermissionKeys = []
      renderWithClient(<Apps />)

      fireEvent.click(screen.getByTestId('open-preview'))
      fireEvent.click(await screen.findByTestId('try-app-create'))

      expect(screen.queryByTestId('create-app-modal')).not.toBeInTheDocument()
    })

    it('should track marketplace template creation after confirming a pending import', async () => {
      mockHandleImportDSL.mockImplementation(
        async (_payload: unknown, options: { onPending?: () => void }) => {
          options.onPending?.()
        },
      )
      mockHandleImportDSLConfirm.mockImplementation(
        async (options: { onSuccess?: (payload: { app_mode: AppModeEnum }) => void }) => {
          options.onSuccess?.({ app_mode: AppModeEnum.WORKFLOW })
        },
      )
      mockSearchParams = new URLSearchParams('template-id=tpl-42')
      renderWithClient(<Apps />)

      fireEvent.click(await screen.findByTestId('confirm-template'))

      await waitFor(() => {
        expect(screen.getByTestId('dsl-confirm-modal')).toBeInTheDocument()
        expect(mockReplace).toHaveBeenCalled()
      })

      fireEvent.click(screen.getByTestId('confirm-dsl'))

      await waitFor(() => {
        expect(mockTrackCreateApp).toHaveBeenCalledWith({
          source: 'external',
          appMode: AppModeEnum.WORKFLOW,
          templateId: 'tpl-42',
        })
      })
    })
  })
})
