import {CRUMB_NAME_MAX, CRUMB_NOTE_MAX} from '@crumbs/core/schemas'
import {Stack} from 'expo-router'
import {useEffect, useMemo, useState} from 'react'
import {ActivityIndicator, View} from 'react-native'

import {PhotoField} from '@/components/composer/photo-field'
import {TagPicker} from '@/components/composer/tag-picker'
import {FormField} from '@/components/forms/form-field'
import {FormSubmit} from '@/components/forms/form-submit'
import * as TextField from '@/components/forms/text-field'
import {CancelButton} from '@/components/ui/cancel-button'
import {Layout} from '@/components/ui/layout'
import {Text} from '@/components/ui/typography'
import {useDiscardGuard} from '@/hooks/use-discard-guard'
import {
  useMediaAction,
  type MediaAction,
} from '@/hooks/use-media-action'
import {useSubmitAction} from '@/hooks/use-submit-action'
import {useTagSuggestions} from '@/hooks/use-tag-suggestions'
import {pickCrumbImage} from '@/lib/crumb-image'
import {hasCanonicalPlace, hasLocation, type Draft} from '@/lib/draft'
import {atoms as a, useTheme} from '@/sprinkles'

// a kept image is left untouched; set uploads a new photo; remove clears it
export type CrumbImageAction = MediaAction

export type CrumbFormFields = {
  name: string
  note: string | null
  address: string | null
  tags: string[]
  image: CrumbImageAction
}

type Props = {
  draft: Draft
  title: string
  submitLabel: string
  inferring?: boolean
  onSubmit: (fields: CrumbFormFields) => Promise<void>
  onCancel: () => void
}

export function CrumbForm({
  draft,
  title,
  submitLabel,
  inferring = false,
  onSubmit,
  onCancel,
}: Props) {
  const t = useTheme()
  const {fromPlace, recent, loading, error, fetch, reset} =
    useTagSuggestions()
  const [name, setName] = useState(draft.name)
  const [note, setNote] = useState(draft.note ?? '')
  const [address, setAddress] = useState(draft.address ?? '')
  const [selected, setSelected] = useState<string[]>(draft.tags)
  const {
    submitting,
    error: submitError,
    submit,
  } = useSubmitAction(onSubmit)
  const image = useMediaAction(draft.imageUrl)
  const canonicalPlace = hasCanonicalPlace(draft)

  const pickImage = async () => {
    const uri = await pickCrumbImage()
    if (!uri) return
    image.set(uri)
  }

  // adopt async-inferred draft values into still-empty fields, never
  // clobbering what the user has typed (adjust state during render)
  const [seeded, setSeeded] = useState({
    name: draft.name,
    address: draft.address,
  })
  if (
    seeded.name !== draft.name ||
    seeded.address !== draft.address
  ) {
    setSeeded({name: draft.name, address: draft.address})
    if (name === '' && draft.name) setName(draft.name)
    if (address === '' && draft.address) setAddress(draft.address)
  }

  useEffect(() => {
    void fetch(draft.category ?? undefined)
    return reset
  }, [draft.category, fetch, reset])

  const toggle = (name: string) =>
    setSelected((cur) =>
      cur.includes(name)
        ? cur.filter((n) => n !== name)
        : [...cur, name],
    )

  const addTag = (name: string) =>
    setSelected((cur) => (cur.includes(name) ? cur : [...cur, name]))

  const suggestions = useMemo(() => {
    const seen = new Set<string>()
    const merged: {name: string}[] = []
    for (const name of [...fromPlace, ...recent]) {
      if (seen.has(name)) continue
      seen.add(name)
      merged.push({name})
    }
    return merged
  }, [fromPlace, recent])

  const valid = name.trim().length > 0

  // seeding keeps inferred values equal to the draft, so inference alone
  // never reads as dirty
  const dirty =
    name !== draft.name ||
    note !== (draft.note ?? '') ||
    address !== (draft.address ?? '') ||
    image.action.action !== 'keep' ||
    selected.join('\n') !== draft.tags.join('\n')
  useDiscardGuard(dirty && !submitting)

  const handleSubmit = () => {
    if (!valid) return
    void submit({
      name: name.trim(),
      note: note.trim() || null,
      address: address.trim() || null,
      tags: selected,
      image: image.action,
    })
  }

  return (
    <>
      <Stack.Screen
        options={{
          title,
          headerLeft: () => <CancelButton onPress={onCancel} />,
          headerRight: () =>
            inferring ? <ActivityIndicator /> : null,
        }}
      />
      <Layout.ModalContent>
        {!hasLocation(draft) && (
          <View style={a.pb_lg}>
            <Text style={t.atoms.text_contrast_medium}>
              Loose crumb — no location
            </Text>
          </View>
        )}

        <FormField label="Name">
          <TextField.Input
            label="Name"
            value={name}
            onChangeText={setName}
            placeholder="Name this crumb"
            maxLength={CRUMB_NAME_MAX}
            editable={!inferring}
          />
        </FormField>

        <FormField label="Photo">
          <PhotoField
            uri={image.uri}
            disabled={inferring}
            onPick={() => void pickImage()}
            onClear={image.clear}
          />
        </FormField>

        {canonicalPlace ? (
          <FormField label="Place">
            <Text emoji>
              {draft.placeRef?.name ?? draft.place?.name}
            </Text>
            {address ? (
              <Text style={[t.atoms.text_contrast_medium]}>
                {address}
              </Text>
            ) : null}
          </FormField>
        ) : (
          <FormField label="Address">
            <TextField.Input
              label="Address"
              value={address}
              onChangeText={setAddress}
              placeholder="e.g. 123 Main St"
              multiline
              editable={!inferring}
            />
          </FormField>
        )}

        <FormField label="Note">
          <TextField.Input
            label="Note"
            value={note}
            onChangeText={setNote}
            placeholder="e.g. Try the pistachio one"
            multiline
            maxLength={CRUMB_NOTE_MAX}
            editable={!inferring}
          />
        </FormField>

        <TagPicker
          suggestions={suggestions}
          loading={loading}
          error={error}
          selected={selected}
          onToggle={toggle}
          onAdd={addTag}
          disabled={inferring}
        />

        <FormSubmit
          label={submitLabel}
          submitting={submitting}
          error={submitError}
          disabled={!valid || submitting}
          onPress={handleSubmit}
        />
      </Layout.ModalContent>
    </>
  )
}
