import { useEffect } from 'react'
import { ControllerRenderProps, UseFormReturn, useWatch } from 'react-hook-form'
import type { CloudProvider } from 'shared-data'
import {
  Badge,
  Select,
  SelectContent,
  SelectGroup,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from 'ui'
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'

import { smartRegionToExactRegion } from './ProjectCreation.utils'
import { useProjectCreationPostgresVersionsQuery } from '@/data/config/project-creation-postgres-versions-query'
import { useProjectUnpausePostgresVersionsQuery } from '@/data/config/project-unpause-postgres-versions-query'
import { PostgresEngine, ReleaseChannel } from '@/data/projects/new-project.constants'
import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'

interface PostgresVersionDetails {
  postgresEngine?: Exclude<PostgresEngine, '13' | '14'>
  releaseChannel?: ReleaseChannel
}

interface PostgresVersionSelectorProps {
  cloudProvider: CloudProvider
  dbRegion: string
  disabled?: boolean
  organizationSlug?: string
  field: ControllerRenderProps<any, 'postgresVersionSelection'>
  form: UseFormReturn<any>
  /**
   * Owned by the form owner (not this component) so the last valid selection
   * survives this selector unmounting, e.g. when its collapsible section
   * closes. Create it with useRef('') alongside the form.
   */
  lastValidSelectionRef: { current: string }
  type?: 'create' | 'unpause'
  layout?: 'vertical' | 'horizontal'
  label?: string
}

const formatValue = ({
  postgres_engine,
  release_channel,
}: {
  postgres_engine: string
  release_channel: string
}) => {
  return `${postgres_engine}|${release_channel}`
}

export const extractPostgresVersionDetails = (value: string): PostgresVersionDetails => {
  if (!value) {
    return { postgresEngine: undefined, releaseChannel: undefined }
  }

  const [postgresEngine, releaseChannel] = value.split('|')
  return { postgresEngine, releaseChannel } as PostgresVersionDetails
}

export const PostgresVersionSelector = ({
  cloudProvider,
  dbRegion,
  disabled = false,
  organizationSlug,
  field,
  form,
  lastValidSelectionRef,
  type = 'create',
  layout = 'horizontal',
  label = 'Postgres version',
}: PostgresVersionSelectorProps) => {
  const { data: project } = useSelectedProjectQuery()

  const dbRegionExact = smartRegionToExactRegion(dbRegion)

  const {
    data: createVersions,
    isPending: isLoadingProjectCreateVersions,
    isSuccess,
  } = useProjectCreationPostgresVersionsQuery(
    {
      cloudProvider,
      dbRegion: dbRegionExact,
      organizationSlug,
    },
    { enabled: type === 'create' }
  )

  const { data: unpauseVersions, isPending: isLoadingProjectUnpauseVersions } =
    useProjectUnpausePostgresVersionsQuery(
      { projectRef: project?.ref },
      { enabled: type === 'unpause' }
    )

  const versions =
    type === 'create'
      ? (createVersions?.available_versions ?? [])
      : (unpauseVersions?.available_versions ?? [])
  const availableVersions = versions.sort((a, b) => a.version.localeCompare(b.version)).reverse()
  const postgresVersionSelection = useWatch({
    control: form.control,
    name: 'postgresVersionSelection',
  })

  // react-hook-form intermittently drops this field's value when its Controller
  // remounts, so a one-shot "set the default once versions load" effect leaves
  // the select stuck empty. Instead this effect re-asserts off the watched value:
  // a selection present in the current list is kept (and remembered in the
  // owner-held lastValidSelectionRef), and when the value is missing or cleared
  // out from under us it restores the last valid selection, falling back to the
  // GA default.
  useEffect(() => {
    if (availableVersions.length === 0) return
    const isSelectionAvailable = (selection: string) =>
      availableVersions.some((version) => formatValue(version) === selection)

    if (postgresVersionSelection && isSelectionAvailable(postgresVersionSelection)) {
      lastValidSelectionRef.current = postgresVersionSelection
      return
    }

    if (isSelectionAvailable(lastValidSelectionRef.current)) {
      form.setValue('postgresVersionSelection', lastValidSelectionRef.current)
      return
    }

    const gaVersion = availableVersions.find((x) => x.release_channel === 'ga')
    const defaultValue = gaVersion ? formatValue(gaVersion) : formatValue(availableVersions[0])
    form.setValue('postgresVersionSelection', defaultValue)
  }, [isSuccess, availableVersions, postgresVersionSelection, lastValidSelectionRef, form])

  return (
    <FormItemLayout id={field.name} label={label} layout={layout}>
      <Select
        value={postgresVersionSelection}
        onValueChange={field.onChange}
        disabled={
          disabled ||
          availableVersions.length === 0 ||
          (type === 'create' && isLoadingProjectCreateVersions) ||
          (type === 'unpause' && isLoadingProjectUnpauseVersions)
        }
      >
        <SelectTrigger
          id={field.name}
          className="[&>:nth-child(1)]:w-full [&>:nth-child(1)]:flex [&>:nth-child(1)]:items-start"
        >
          <SelectValue placeholder="Select a Postgres version for your project" />
        </SelectTrigger>
        <SelectContent>
          <SelectGroup>
            {availableVersions.map((value) => {
              const postgresVersion = value.version
                .split('supabase-postgres-')[1]
                .replace('-orioledb', '')
              return (
                <SelectItem
                  key={formatValue(value)}
                  value={formatValue(value)}
                  className="w-full [&>:nth-child(2)]:w-full"
                >
                  <div className="flex flex-row items-center justify-between w-full">
                    <span className="text-foreground">{postgresVersion}</span>
                    <div className="flex flex-row gap-x-2">
                      {value.release_channel !== 'ga' && (
                        <Badge variant="warning">{value.release_channel}</Badge>
                      )}
                      {value.postgres_engine.includes('oriole') && (
                        <Badge variant="default">OrioleDB</Badge>
                      )}
                    </div>
                  </div>
                </SelectItem>
              )
            })}
          </SelectGroup>
        </SelectContent>
      </Select>
    </FormItemLayout>
  )
}
