import type { HumanInputFieldValue } from './field-renderer'
import type { FileEntity } from '@/app/components/base/file-uploader/types'
import type { FormInputItem } from '@/app/components/workflow/nodes/human-input/types'
import type { Locale } from '@/i18n-config'
import type { HumanInputResolvedValue } from '@/types/workflow'
import dayjs from 'dayjs'
import isSameOrAfter from 'dayjs/plugin/isSameOrAfter'
import relativeTime from 'dayjs/plugin/relativeTime'
import utc from 'dayjs/plugin/utc'
import { fileIsUploaded, getProcessedFiles } from '@/app/components/base/file-uploader/utils'
import {
  isFileFormInput,
  isFileListFormInput,
  isParagraphFormInput,
  isSelectFormInput,
  UserActionButtonType,
} from '@/app/components/workflow/nodes/human-input/types'
import 'dayjs/locale/en'
import 'dayjs/locale/zh-cn'
import 'dayjs/locale/ja'
import 'dayjs/locale/nl'

dayjs.extend(utc)
dayjs.extend(relativeTime)
dayjs.extend(isSameOrAfter)

export const getButtonStyle = (style: UserActionButtonType) => {
  if (style === UserActionButtonType.Primary) return 'primary'
  if (style === UserActionButtonType.Default) return 'secondary'
  if (style === UserActionButtonType.Accent) return 'secondary-accent'
  if (style === UserActionButtonType.Ghost) return 'ghost'
}

export const splitByOutputVar = (content: string): string[] => {
  const outputVarRegex = /(\{\{#\$output\.[^#]+#\}\})/g
  const parts = content.split(outputVarRegex)
  return parts.filter((part) => part.length > 0)
}

export const getFormContentInputNames = (content: string) => {
  const outputVarRegex = /\{\{#\$output\.([^#]+)#\}\}/g
  return [...content.matchAll(outputVarRegex)].map((match) => match[1]!)
}

export const getRenderedFormInputs = (formInputs: FormInputItem[], content: string) => {
  const inputNames = new Set(getFormContentInputNames(content))
  return formInputs.filter((input) => inputNames.has(input.output_variable_name))
}

export const initializeInputs = (
  formInputs: FormInputItem[],
  defaultValues: Record<string, HumanInputResolvedValue> = {},
) => {
  const initialInputs: Record<string, HumanInputFieldValue> = {}
  formInputs.forEach((item) => {
    if (isParagraphFormInput(item)) {
      const resolvedValue = defaultValues[item.output_variable_name]
      initialInputs[item.output_variable_name] =
        item.default.type === 'variable' && typeof resolvedValue === 'string'
          ? resolvedValue
          : item.default.value
      return
    }

    if (isSelectFormInput(item)) {
      initialInputs[item.output_variable_name] = ''
      return
    }

    if (isFileFormInput(item)) {
      initialInputs[item.output_variable_name] = null
      return
    }

    if (isFileListFormInput(item)) {
      initialInputs[item.output_variable_name] = []
    }
  })
  return initialInputs
}

const isHumanInputFileUploaded = (value: HumanInputFieldValue | undefined) => {
  return (
    !!value &&
    !Array.isArray(value) &&
    typeof value !== 'string' &&
    !!fileIsUploaded(value as FileEntity)
  )
}

const hasUploadedHumanInputFiles = (value: HumanInputFieldValue | undefined) => {
  return Array.isArray(value) && value.length > 0 && value.every((file) => !!fileIsUploaded(file))
}

export const hasInvalidSelectOrFileInput = (
  formInputs: FormInputItem[],
  values: Record<string, HumanInputFieldValue>,
) => {
  return formInputs.some((input) => {
    if (!(input.output_variable_name in values)) return false

    const value = values[input.output_variable_name]

    if (isSelectFormInput(input)) return typeof value !== 'string' || value.length === 0

    if (isFileFormInput(input))
      return Array.isArray(value)
        ? !hasUploadedHumanInputFiles(value)
        : !isHumanInputFileUploaded(value)

    if (isFileListFormInput(input)) return !hasUploadedHumanInputFiles(value)

    return false
  })
}

export const hasInvalidRequiredHumanInput = (
  formInputs: FormInputItem[],
  values: Record<string, HumanInputFieldValue>,
) => {
  return formInputs.some((input) => {
    if (!(input.output_variable_name in values)) return false

    const value = values[input.output_variable_name]

    if (isParagraphFormInput(input)) return typeof value !== 'string' || value.trim().length === 0

    if (isSelectFormInput(input)) return typeof value !== 'string' || value.length === 0

    if (isFileFormInput(input))
      return Array.isArray(value)
        ? !hasUploadedHumanInputFiles(value)
        : !isHumanInputFileUploaded(value)

    if (isFileListFormInput(input)) return !hasUploadedHumanInputFiles(value)

    return false
  })
}

export const getProcessedHumanInputFormInputs = (
  formInputs: FormInputItem[],
  values: Record<string, HumanInputFieldValue> | undefined,
) => {
  if (!values) return undefined

  const processedInputs: Record<string, unknown> = { ...values }

  formInputs.forEach((input) => {
    const value = values[input.output_variable_name]

    if (isFileListFormInput(input)) {
      processedInputs[input.output_variable_name] = Array.isArray(value)
        ? getProcessedFiles(value)
        : []
      return
    }

    if (isFileFormInput(input)) {
      if (Array.isArray(value)) {
        processedInputs[input.output_variable_name] = getProcessedFiles(value)[0]
        return
      }

      processedInputs[input.output_variable_name] =
        value && typeof value !== 'string' ? getProcessedFiles([value as FileEntity])[0] : undefined
    }
  })

  return processedInputs
}

const localeMap: Record<string, string> = {
  'en-US': 'en',
  'zh-Hans': 'zh-cn',
  'ja-JP': 'ja',
  'nl-NL': 'nl',
}

export const getRelativeTime = (utcTimestamp: string | number, locale: Locale = 'en-US') => {
  const dayjsLocale = localeMap[locale] ?? 'en'

  return dayjs.utc(utcTimestamp).locale(dayjsLocale).fromNow()
}

export const isRelativeTimeSameOrAfter = (utcTimestamp: string | number) => {
  return dayjs.utc(utcTimestamp).isSameOrAfter(dayjs())
}
