import { ident, literal, safeSql, type SafeSqlFragment } from '@supabase/pg-meta/src/pg-format'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { noop } from 'lodash'
import { Loader } from 'lucide-react'
import { useState } from 'react'
import { toast } from 'sonner'
import { Button, ExpandingTextArea } from 'ui'

import { executeSql } from '@/data/sql/execute-sql-query'
import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
import { timeout } from '@/lib/helpers'

// Removes some auto-generated Postgrest text
// Ideally PostgREST wouldn't add this if there is already a comment
const temp_removePostgrestText = (content: string) => {
  const postgrestTextPk = `Note:\nThis is a Primary Key.<pk/>`
  const postgrestTextFk = `Note:\nThis is a Foreign Key to`
  const pkTextPos = content.lastIndexOf(postgrestTextPk)
  const fkTextPos = content.lastIndexOf(postgrestTextFk)

  let cleansed = content
  if (pkTextPos >= 0) cleansed = cleansed.substring(0, pkTextPos)
  if (fkTextPos >= 0) cleansed = cleansed.substring(0, fkTextPos)
  return cleansed
}

interface DescrptionProps {
  content: string
  metadata: { table?: string; column?: string; rpc?: string }
  onChange: (value: string) => void
}

const Description = ({ content, metadata, onChange = noop }: DescrptionProps) => {
  const contentText = temp_removePostgrestText(content || '').trim()
  const [value, setValue] = useState(contentText)
  const [isUpdating, setIsUpdating] = useState(false)
  const { data: project } = useSelectedProjectQuery()

  const { table, column, rpc } = metadata

  const hasChanged = value != contentText
  const animateCss = `transition duration-150`

  const { can: canUpdateDescription } = useAsyncCheckPermissions(
    PermissionAction.TENANT_SQL_QUERY,
    '*'
  )

  const updateDescription = async () => {
    if (isUpdating || !canUpdateDescription) return false

    setIsUpdating(true)
    let query: SafeSqlFragment | undefined
    if (table && column)
      query = safeSql`comment on column ${ident('public')}.${ident(table)}.${ident(column)} is ${literal(value)};`
    if (table && !column)
      query = safeSql`comment on table ${ident('public')}.${ident(table)} is ${literal(value)};`
    if (rpc) query = safeSql`comment on function ${ident(rpc)} is ${literal(value)};`

    if (query) {
      try {
        await executeSql({
          projectRef: project?.ref,
          connectionString: project?.connectionString,
          sql: query,
        })
        // [Joshen] Temp fix, immediately refreshing the docs fetches stale state
        await timeout(500)
        toast.success(`Successfully updated description`)
      } catch (error: any) {
        toast.error(`Failed to update description: ${error.message}`)
      }
    }

    onChange(value)
    setIsUpdating(false)
  }

  if (!canUpdateDescription) {
    return (
      <span className={`block text-sm ${value ? 'text-foreground' : ''}`}>
        {value || 'No description'}
      </span>
    )
  }

  return (
    <div className="space-y-2 px-0.5">
      <ExpandingTextArea
        className="w-full min-h-auto"
        placeholder="Click to edit."
        value={value}
        onChange={(e: any) => setValue(e.target.value)}
      />
      <div
        className={`flex items-center gap-2 ${
          hasChanged ? 'opacity-100' : 'h-0 cursor-default opacity-0'
        } ${animateCss}`}
      >
        <Button
          type="default"
          disabled={!hasChanged}
          onClick={() => {
            setValue(contentText)
            setIsUpdating(false)
          }}
        >
          Cancel
        </Button>
        <Button disabled={!hasChanged} onClick={updateDescription}>
          {isUpdating ? (
            <Loader className="mx-auto animate-spin" size={14} strokeWidth={2} />
          ) : (
            <span>Save</span>
          )}
        </Button>
      </div>
    </div>
  )
}

export default Description
