import { PermissionAction } from '@supabase/shared-types/out/constants'
import { Loader2 } from 'lucide-react'
import Link from 'next/link'
import { useEffect, useState } from 'react'
import { Alert, AlertDescription, AlertTitle, Button } from 'ui'
import { Admonition } from 'ui-patterns/admonition'
import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader'

import { PreviousRestoreItem } from './PreviousRestoreItem'
import { PITRForm } from '@/components/interfaces/Database/Backups/PITR/PITRForm'
import { BackupsList } from '@/components/interfaces/Database/Backups/RestoreToNewProject/BackupsList'
import { ConfirmRestoreDialog } from '@/components/interfaces/Database/Backups/RestoreToNewProject/ConfirmRestoreDialog'
import { CreateNewProjectDialog } from '@/components/interfaces/Database/Backups/RestoreToNewProject/CreateNewProjectDialog'
import { projectSpecToMonthlyPrice } from '@/components/interfaces/Database/Backups/RestoreToNewProject/RestoreToNewProject.utils'
import { DiskType } from '@/components/interfaces/DiskManagement/ui/DiskManagement.constants'
import { Markdown } from '@/components/interfaces/Markdown'
import AlertError from '@/components/ui/AlertError'
import { InlineLink } from '@/components/ui/InlineLink'
import NoPermission from '@/components/ui/NoPermission'
import Panel from '@/components/ui/Panel'
import { UpgradeToPro } from '@/components/ui/UpgradeToPro'
import { useDiskAttributesQuery } from '@/data/config/disk-attributes-query'
import { useCloneBackupsQuery } from '@/data/projects/clone-query'
import { useCloneStatusQuery } from '@/data/projects/clone-status-query'
import { useCheckEntitlements } from '@/hooks/misc/useCheckEntitlements'
import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
import {
  useIsAwsK8sCloudProvider,
  useIsOrioleDb,
  useSelectedProjectQuery,
} from '@/hooks/misc/useSelectedProject'
import { DOCS_URL, PROJECT_STATUS } from '@/lib/constants'
import { getDatabaseMajorVersion } from '@/lib/helpers'

export const RestoreToNewProject = () => {
  const { data: project } = useSelectedProjectQuery()
  const { data: organization } = useSelectedOrganizationQuery()
  const { hasAccess: hasAccessToRestoreToNewProject, isLoading: isLoadingEntitlement } =
    useCheckEntitlements('backup.restore_to_new_project')
  const isOrioleDb = useIsOrioleDb()
  const isAwsK8s = useIsAwsK8sCloudProvider()

  const [refetchInterval, setRefetchInterval] = useState<number | false>(false)
  const [selectedBackupId, setSelectedBackupId] = useState<number | null>(null)
  const [showConfirmationDialog, setShowConfirmationDialog] = useState(false)
  const [showNewProjectDialog, setShowNewProjectDialog] = useState(false)
  const [recoveryTimeTarget, setRecoveryTimeTarget] = useState<number | null>(null)

  const {
    data: cloneBackups,
    error,
    isPending: cloneBackupsLoading,
    isError,
  } = useCloneBackupsQuery(
    { projectRef: project?.ref },
    { enabled: hasAccessToRestoreToNewProject }
  )

  const isActiveHealthy = project?.status === PROJECT_STATUS.ACTIVE_HEALTHY

  const { can: canReadPhysicalBackups, isSuccess: isPermissionsLoaded } = useAsyncCheckPermissions(
    PermissionAction.READ,
    'physical_backups'
  )
  const { can: canTriggerPhysicalBackups } = useAsyncCheckPermissions(
    PermissionAction.INFRA_EXECUTE,
    'queue_job.restore.prepare'
  )
  const PITR_ENABLED = cloneBackups?.pitr_enabled
  const PHYSICAL_BACKUPS_ENABLED = project?.is_physical_backups_enabled
  const dbVersion = getDatabaseMajorVersion(project?.dbVersion ?? '')
  const IS_PG15_OR_ABOVE = dbVersion >= 15
  const targetVolumeSizeGb = cloneBackups?.target_volume_size_gb
  const targetComputeSize = cloneBackups?.target_compute_size
  const planId = organization?.plan?.id ?? 'free'
  const { data } = useDiskAttributesQuery({ projectRef: project?.ref })
  const storageType = data?.attributes?.type ?? 'gp3'

  const {
    data: cloneStatus,
    refetch: refetchCloneStatus,
    isPending: cloneStatusLoading,
    isSuccess: isCloneStatusSuccess,
  } = useCloneStatusQuery(
    {
      projectRef: project?.ref,
    },
    {
      refetchInterval,
      refetchOnWindowFocus: false,
      enabled: PHYSICAL_BACKUPS_ENABLED || PITR_ENABLED,
    }
  )
  const isLoading = !isPermissionsLoaded || cloneBackupsLoading || cloneStatusLoading

  useEffect(() => {
    if (!isCloneStatusSuccess) return
    const hasTransientState = cloneStatus.clones.some((c) => c.status === 'IN_PROGRESS')
    if (!hasTransientState) {
      setRefetchInterval(false)
    }
  }, [cloneStatus?.clones, isCloneStatusSuccess])

  const previousClones = cloneStatus?.clones
  const isRestoring = previousClones?.some((c) => c.status === 'IN_PROGRESS')
  const restoringClone = previousClones?.find((c) => c.status === 'IN_PROGRESS')

  if (isLoadingEntitlement) {
    return <GenericSkeletonLoader />
  }

  if (!hasAccessToRestoreToNewProject) {
    return (
      <UpgradeToPro
        buttonText="Upgrade"
        source="backupsRestoreToNewProject"
        featureProposition="enable restoring to new project"
        primaryText="Restore to a new project requires Pro Plan and above"
        secondaryText="To restore to a new project, you need to upgrade to a Pro Plan and have physical backups enabled."
      />
    )
  }

  if (isOrioleDb) {
    return (
      <Admonition
        type="default"
        title="Restoring to new projects are not available for OrioleDB"
        description="OrioleDB is currently in public alpha and projects created are strictly ephemeral with no database backups"
      />
    )
  }

  if (isAwsK8s) {
    return (
      <Admonition
        type="default"
        description="Restoring to new projects is temporarily not available for AWS (Revamped) projects."
      />
    )
  }

  if (!canReadPhysicalBackups) {
    return <NoPermission resourceText="view backups" />
  }

  if (!canTriggerPhysicalBackups) {
    return <NoPermission resourceText="restore backups" />
  }

  if (!IS_PG15_OR_ABOVE) {
    return (
      <Admonition
        type="default"
        title="Restore to new project is not available for this database version"
      >
        <Markdown
          className="max-w-full"
          content={`Restore to new project is only available for Postgres 15 and above.  
            Go to [infrastructure settings](/project/${project?.ref}/settings/infrastructure)
            to upgrade your database version.
          `}
        />
      </Admonition>
    )
  }

  if (!PHYSICAL_BACKUPS_ENABLED) {
    return (
      <Admonition
        type="default"
        title="Physical backups are required"
        description={
          <>
            Physical backups must be enabled to restore your database to a new project.{' '}
            <InlineLink href={`${DOCS_URL}/guides/platform/backups`}>Learn more</InlineLink>
          </>
        }
      />
    )
  }

  if (isLoading) {
    return <GenericSkeletonLoader />
  }

  if (isError) {
    return <AlertError error={error} subject="Failed to retrieve backups" />
  }

  if (!isActiveHealthy) {
    return (
      <Admonition
        type="default"
        title="Restore to new project is not available while project is offline"
        description="Your project needs to be online to restore your database to a new project"
      />
    )
  }

  if (
    !isLoading &&
    PITR_ENABLED &&
    !cloneBackups?.physicalBackupData.earliestPhysicalBackupDateUnix
  ) {
    return (
      <Admonition
        type="default"
        title="No backups found"
        description="PITR is enabled, but no backups were found. Check again in a few minutes."
      />
    )
  }

  if (!isLoading && !PITR_ENABLED && cloneBackups?.backups.length === 0) {
    return (
      <>
        <Admonition
          type="default"
          title="No backups found"
          description="Backups are enabled, but no backups were found. Check again tomorrow."
        />
      </>
    )
  }

  const additionalMonthlySpend = projectSpecToMonthlyPrice({
    targetVolumeSizeGb: targetVolumeSizeGb ?? 0,
    targetComputeSize: targetComputeSize ?? 'nano',
    planId: planId ?? 'free',
    storageType: storageType as DiskType,
  })

  return (
    <div className="flex flex-col gap-4">
      <ConfirmRestoreDialog
        open={showConfirmationDialog}
        onOpenChange={setShowConfirmationDialog}
        onSelectContinue={() => {
          setShowConfirmationDialog(false)
          setShowNewProjectDialog(true)
        }}
        additionalMonthlySpend={additionalMonthlySpend}
      />
      <CreateNewProjectDialog
        open={showNewProjectDialog}
        selectedBackupId={selectedBackupId}
        recoveryTimeTarget={recoveryTimeTarget}
        additionalMonthlySpend={additionalMonthlySpend}
        hasAccess={hasAccessToRestoreToNewProject}
        onOpenChange={setShowNewProjectDialog}
        onCloneSuccess={() => {
          refetchCloneStatus()
          setRefetchInterval(5000)
          setShowNewProjectDialog(false)
        }}
      />
      {isRestoring ? (
        <Alert className="[&>svg]:bg-none! [&>svg]:text-foreground-light mb-6">
          <Loader2 className="animate-spin" />
          <AlertTitle>Restoration in progress</AlertTitle>
          <AlertDescription>
            <p>
              The new project {(restoringClone?.target_project as any)?.name || ''} is currently
              being created. You'll be able to restore again once the project is ready.
            </p>
            <Button asChild type="default" className="mt-2">
              <Link href={`/project/${restoringClone?.target_project?.ref ?? '_'}`}>
                Go to new project
              </Link>
            </Button>
          </AlertDescription>
        </Alert>
      ) : null}
      {previousClones?.length ? (
        <div className="flex flex-col gap-2">
          <h3 className="text-sm font-medium">Previous restorations</h3>
          <Panel className="flex flex-col divide-y divide-border">
            {previousClones?.map((c) => (
              <PreviousRestoreItem key={c.inserted_at} clone={c} />
            ))}
          </Panel>
        </div>
      ) : null}
      {PITR_ENABLED ? (
        <>
          <PITRForm
            disabled={isRestoring}
            onSubmit={(v) => {
              setShowConfirmationDialog(true)
              setRecoveryTimeTarget(v.recoveryTimeTargetUnix)
            }}
            earliestAvailableBackupUnix={
              cloneBackups?.physicalBackupData.earliestPhysicalBackupDateUnix || 0
            }
            latestAvailableBackupUnix={
              cloneBackups?.physicalBackupData.latestPhysicalBackupDateUnix || 0
            }
          />
        </>
      ) : (
        <BackupsList
          disabled={isRestoring}
          hasAccess={hasAccessToRestoreToNewProject}
          onSelectRestore={(id) => {
            setSelectedBackupId(id)
            setShowConfirmationDialog(true)
          }}
        />
      )}
    </div>
  )
}
