'use client'

import type { GuideMethod, WorkflowSourceApp } from '@/features/deployments/create-guide/state/types'
import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import { Input } from '@langgenius/dify-ui/input'
import { RadioRoot } from '@langgenius/dify-ui/radio'
import { RadioGroup } from '@langgenius/dify-ui/radio-group'
import { useAtomValue, useSetAtom } from 'jotai'
import { useTranslation } from 'react-i18next'
import Uploader from '@/app/components/app/create-from-dsl-modal/uploader'
import AppIcon from '@/app/components/base/app-icon'
import { SkeletonRectangle, SkeletonRow } from '@/app/components/base/skeleton'
import {
  dslFileAtom,
  effectiveMethodAtom,
  sourceSearchTextAtom,
} from '@/features/deployments/create-guide/state/primitives'
import { unsupportedDslNodesAtom } from '@/features/deployments/create-guide/state/queries'
import {
  dslReadErrorAtom,
  dslUnsupportedModeAtom,
  effectiveSelectedAppAtom,
  isReadingDslAtom,
  sourceAppsQueryAtom,
} from '@/features/deployments/create-guide/state/source'
import {
  continueFromSourceAtom,
  selectDslFileAtom,
  selectMethodAtom,
  selectSourceAppAtom,
  setSourceSearchTextAtom,
  sourceCanGoNextAtom,
} from '@/features/deployments/create-guide/state/workflow'
import { DeploymentStateMessage } from '@/features/deployments/shared/components/empty-state'
import { TitleTooltip } from '@/features/deployments/shared/components/title-tooltip'
import { UnsupportedDslNodesAlert } from '@/features/deployments/shared/components/unsupported-dsl-nodes-alert'
import { isDeploymentDslImportEnabled } from '@/features/deployments/shared/domain/feature-flags'
import { useInfiniteScroll } from '@/features/deployments/shared/hooks/use-infinite-scroll'
import { StepShell } from './layout'

const sourceAppSkeletonKeys = ['first-source-app', 'second-source-app', 'third-source-app']

export function SourceStepContent() {
  const method = useAtomValue(effectiveMethodAtom)
  const unsupportedDslNodes = useAtomValue(unsupportedDslNodesAtom)

  return (
    <div className="flex min-h-0 flex-1 flex-col gap-4">
      <SourceMethodSection />
      {method === 'bindApp' && (
        <SourceAppSelectionSection />
      )}
      {method === 'importDsl' && (
        <DslUploadSection />
      )}
      <UnsupportedDslNodesAlert nodes={unsupportedDslNodes} />
    </div>
  )
}

function SourceMethodSection() {
  const { t } = useTranslation('deployments')
  const method = useAtomValue(effectiveMethodAtom)
  const selectMethod = useSetAtom(selectMethodAtom)

  return (
    <StepShell
      title={t('createGuide.steps.method')}
      description={t('createGuide.method.description')}
      descriptionClassName="lg:hidden"
      hideHeader
    >
      <RadioGroup<GuideMethod>
        value={method}
        onValueChange={selectMethod}
        className="flex flex-col items-stretch gap-2 sm:flex-row"
      >
        <SourceMethodCard
          value="bindApp"
          icon="i-ri-stack-line"
          title={t('createGuide.methods.bindApp.title')}
          description={t('createGuide.methods.bindApp.description')}
        />
        {isDeploymentDslImportEnabled && (
          <SourceMethodCard
            value="importDsl"
            icon="i-ri-file-code-line"
            title={t('createGuide.methods.importDsl.title')}
            description={t('createGuide.methods.importDsl.description')}
          />
        )}
      </RadioGroup>
    </StepShell>
  )
}

function SourceMethodCard({ value, icon, title, description, badge }: {
  value: GuideMethod
  icon: string
  title: string
  description: string
  badge?: string
}) {
  return (
    <RadioRoot<GuideMethod>
      value={value}
      variant="unstyled"
      className={cn(
        `relative box-content h-[84px] w-full cursor-pointer rounded-xl border-[0.5px]
        border-components-option-card-option-border bg-components-panel-on-panel-item-bg p-3
        text-left shadow-xs outline-hidden hover:shadow-md focus-visible:ring-2
        focus-visible:ring-state-accent-solid sm:w-[240px]`,
        'data-checked:border-components-option-card-option-selected-border data-checked:bg-components-option-card-option-selected-bg data-checked:shadow-md data-checked:inset-ring-[0.5px] data-checked:inset-ring-components-option-card-option-selected-border',
      )}
    >
      <span className="flex size-6 shrink-0 items-center justify-center rounded-md border border-divider-subtle bg-background-default-subtle">
        <span className={cn('size-4 text-text-tertiary', icon)} aria-hidden="true" />
      </span>
      <span className="mt-2 mb-0.5 flex min-w-0 items-center gap-1">
        <span className="truncate system-sm-semibold text-text-secondary">{title}</span>
        {badge && (
          <span className="shrink-0 rounded-md bg-background-default-subtle px-1.5 py-0.5 system-2xs-medium-uppercase text-text-tertiary">
            {badge}
          </span>
        )}
      </span>
      <span className="flex min-w-0 items-start gap-1">
        <TitleTooltip content={description}>
          <span className="line-clamp-2 min-w-0 grow system-xs-regular text-text-tertiary">
            {description}
          </span>
        </TitleTooltip>
      </span>
    </RadioRoot>
  )
}

function SourceAppSelectionSection() {
  const { t } = useTranslation('deployments')

  return (
    <StepShell
      title={t('createGuide.source.title')}
      description={t('createGuide.source.description')}
      descriptionClassName="lg:hidden"
      hideHeader
      className="min-h-0 flex-1"
    >
      <div className="flex min-h-0 flex-1 flex-col gap-3">
        <SourceSearchInput />
        <SourceAppList />
      </div>
    </StepShell>
  )
}

function SourceSearchInput() {
  const { t } = useTranslation('deployments')
  const sourceSearchText = useAtomValue(sourceSearchTextAtom)
  const setSourceSearchText = useSetAtom(setSourceSearchTextAtom)

  return (
    <div className="relative">
      <span className="pointer-events-none absolute top-1/2 left-2.5 i-ri-search-line size-4 -translate-y-1/2 text-text-tertiary" aria-hidden="true" />
      <Input
        id="create-guide-source-search"
        aria-label={t('createGuide.source.sourceApp')}
        value={sourceSearchText}
        onChange={event => setSourceSearchText(event.target.value)}
        placeholder={t('createGuide.source.searchPlaceholder')}
        className="h-9 pr-8 pl-8"
      />
      {sourceSearchText && (
        <button
          type="button"
          aria-label={t('createGuide.source.clearSearch')}
          onClick={() => setSourceSearchText('')}
          className="absolute top-1/2 right-2.5 flex size-4 -translate-y-1/2 items-center justify-center text-text-quaternary hover:text-text-secondary"
        >
          <span className="i-ri-close-circle-fill size-4" aria-hidden="true" />
        </button>
      )}
    </div>
  )
}

function SourceAppList() {
  const { t } = useTranslation('deployments')
  const selectSourceApp = useSetAtom(selectSourceAppAtom)
  const effectiveSelectedApp = useAtomValue(effectiveSelectedAppAtom)
  const sourceAppsQuery = useAtomValue(sourceAppsQueryAtom)
  const sourceApps = (sourceAppsQuery.data?.pages.flatMap(page => page.data) ?? []) as WorkflowSourceApp[]
  const sourceAppsLoading = sourceAppsQuery.isLoading || sourceAppsQuery.isPlaceholderData || (sourceAppsQuery.isFetching && sourceApps.length === 0)
  const { rootRef, sentinelRef } = useInfiniteScroll<HTMLDivElement>(sourceAppsQuery, {
    enabled: !sourceAppsLoading,
    rootMargin: '0px 0px 160px 0px',
    threshold: 0.1,
  })

  return (
    <div ref={rootRef} className="min-h-0 flex-1 overflow-y-auto rounded-lg border border-divider-subtle bg-background-default">
      {sourceAppsLoading
        ? <SourceAppSkeleton />
        : sourceApps.length === 0
          ? (
              <DeploymentStateMessage variant="embedded">
                {t('createGuide.source.empty')}
              </DeploymentStateMessage>
            )
          : (
              <div>
                {sourceApps.map(app => (
                  <SourceAppOption
                    key={app.id}
                    app={app}
                    selected={effectiveSelectedApp?.id === app.id}
                    onSelect={() => selectSourceApp(app)}
                  />
                ))}
                {sourceAppsQuery.isFetchingNextPage && (
                  <div className="border-t border-divider-subtle px-3 py-2 text-center system-xs-regular text-text-tertiary">
                    {t('createModal.loadingApps')}
                  </div>
                )}
                {sourceAppsQuery.hasNextPage && <div ref={sentinelRef} aria-hidden="true" className="h-px" />}
              </div>
            )}
    </div>
  )
}

function SourceAppSkeleton() {
  return (
    <div className="divide-y divide-divider-subtle">
      {sourceAppSkeletonKeys.map(key => (
        <SkeletonRow key={key} className="h-14 px-3 py-2">
          <SkeletonRectangle className="my-0 size-7 animate-pulse rounded-lg" />
          <div className="flex min-w-0 grow flex-col gap-1">
            <SkeletonRectangle className="my-0 h-3.5 w-2/3 animate-pulse" />
            <SkeletonRectangle className="my-0 h-2.5 w-1/3 animate-pulse" />
          </div>
        </SkeletonRow>
      ))}
    </div>
  )
}

function SourceAppOption({ app, onSelect, selected }: {
  app: WorkflowSourceApp
  onSelect: () => void
  selected: boolean
}) {
  return (
    <label
      className={cn(
        'group flex min-h-14 cursor-pointer items-center gap-3 border-b border-b-divider-subtle px-3 py-2 transition-colors first:rounded-t-lg last:rounded-b-lg last:border-b-0',
        selected
          ? 'bg-state-accent-hover hover:bg-state-accent-hover'
          : 'bg-background-default hover:bg-state-base-hover',
      )}
    >
      <AppIcon
        className="shrink-0"
        size="xs"
        iconType={app.icon_type}
        icon={app.icon}
        background={app.icon_background}
        imageUrl={app.icon_url}
      />
      <span className="flex min-w-0 grow">
        <span className={cn('truncate system-sm-medium', selected ? 'text-text-accent' : 'text-text-primary')}>{app.name}</span>
      </span>
      <input
        type="radio"
        name="source-app"
        checked={selected}
        onChange={onSelect}
        className="sr-only"
      />
      <span
        className={cn(
          'flex size-5 shrink-0 items-center justify-center rounded-full',
          selected ? 'bg-primary-600 text-text-primary-on-surface' : 'text-transparent',
        )}
        aria-hidden="true"
      >
        <span className="i-ri-check-line size-4" />
      </span>
    </label>
  )
}

function DslUploadSection() {
  const { t } = useTranslation('deployments')
  const dslFile = useAtomValue(dslFileAtom)
  const selectDslFile = useSetAtom(selectDslFileAtom)

  return (
    <StepShell title={t('createGuide.dsl.title')} description={t('createGuide.dsl.description')} hideHeader>
      <div className="flex flex-col gap-4 rounded-xl border border-components-panel-border bg-components-panel-bg-blur p-5">
        <div className="flex items-start gap-3">
          <span className="mt-0.5 i-ri-upload-cloud-2-line size-5 shrink-0 text-text-tertiary" aria-hidden="true" />
          <div className="flex min-w-0 flex-col gap-1">
            <div className="system-sm-semibold text-text-primary">{t('createGuide.dsl.dropTitle')}</div>
            <div className="system-sm-regular text-text-tertiary">{t('createGuide.dsl.dropDescription')}</div>
          </div>
        </div>
        <Uploader
          className="mt-0"
          file={dslFile}
          updateFile={selectDslFile}
        />
        <DslReadStatus />
      </div>
    </StepShell>
  )
}

function DslReadStatus() {
  const { t } = useTranslation('deployments')
  const isReadingDsl = useAtomValue(isReadingDslAtom)
  const dslReadError = useAtomValue(dslReadErrorAtom)
  const dslUnsupportedMode = useAtomValue(dslUnsupportedModeAtom)

  return (
    <>
      {isReadingDsl && (
        <div className="system-xs-regular text-text-tertiary">
          {t('createGuide.dsl.reading')}
        </div>
      )}
      {dslReadError && (
        <div className="system-xs-regular text-text-destructive">
          {t('createGuide.dsl.readFailed')}
        </div>
      )}
      {dslUnsupportedMode && (
        <div role="alert" className="system-xs-regular text-text-destructive">
          {t('createGuide.dsl.unsupportedMode')}
        </div>
      )}
    </>
  )
}

export function SourceActionButtons() {
  const { t } = useTranslation('deployments')
  const canGoNext = useAtomValue(sourceCanGoNextAtom)
  const continueFromSource = useSetAtom(continueFromSourceAtom)

  return (
    <Button
      type="button"
      variant="primary"
      disabled={!canGoNext}
      onClick={() => continueFromSource({
        defaultDslAppName: t('createGuide.dsl.defaultAppName'),
        defaultReleaseName: t('createGuide.release.defaultName'),
      })}
    >
      {t('createGuide.actions.next')}
    </Button>
  )
}
