import { useState, type KeyboardEvent } from 'react'
import { Alert } from '@/components/ui/Alert'
import { Button, IconButton } from '@/components/ui/Button'
import { TextAreaField, TextField } from '@/components/ui/Field'
import { errorMessage } from '@/lib/api'
import { cn } from '@/lib/cn'
import { today } from '@/lib/dates'
import {
  formatObservedAt,
  useCreateJournalEntry,
  useDeleteJournalEntry,
  useJournal,
  useUpdateJournalEntry,
  type JournalEntry,
  type JournalFilter,
} from '@/lib/journal'
import type { EditorPlanting } from '@/lib/plantings'
import { objectDisplayName } from './kinds'
import { useEditorStore } from './store'
import type { EditorObject } from './types'

/** What a new note attaches to: the selection, else the focused bed, else the
 *  garden — and how to say it. */
export interface JournalAttach {
  objectId?: number
  plantingId?: number
  label: string | null
}

/**
 * The grow journal: entries newest first as cards (what it's about, when, the
 * note), a one-line composer that attaches to whatever's selected — Enter
 * submits — and, when the rail was opened from a bed's "N notes", a filter chip
 * narrowing the list to that thing. Notes get written standing in the garden
 * holding a phone, so the composer is never more than a tap away.
 */
export function JournalTab({
  gardenId,
  canEdit,
  currentUserId,
  isOwner,
  objects,
  plantings,
  attach,
  composerFirst = false,
  large = false,
}: {
  gardenId: number
  canEdit: boolean
  currentUserId?: number
  isOwner: boolean
  objects: EditorObject[]
  plantings: EditorPlanting[]
  attach: JournalAttach
  /** Phone: the input above the entries (the thumb is at the bottom anyway). */
  composerFirst?: boolean
  large?: boolean
}) {
  const scope = useEditorStore((s) => s.journalScope)
  const setScope = useEditorStore((s) => s.setJournalScope)
  const filter: JournalFilter = scope?.type === 'object' ? { objectId: scope.id } : scope?.type === 'plop' ? { plantingId: scope.id } : {}
  const journal = useJournal(gardenId, filter)
  const entries = journal.data?.pages.flatMap((p) => p.entries) ?? []
  const scopeName =
    scope?.type === 'object'
      ? objectDisplayName(objects.find((o) => o.id === scope.id) ?? { name: '', kind: 'object' })
      : scope?.type === 'plop'
        ? 'one planting'
        : null

  const composer = canEdit && (
    <Composer gardenId={gardenId} attach={attach} large={large} />
  )

  return (
    <div className="flex min-h-0 flex-1 flex-col gap-2.5">
      {scopeName && (
        <div className="flex items-center gap-2 rounded-full bg-accent-2-200 py-1 pl-3.5 pr-1 text-xs font-semibold text-accent-2-800">
          Notes about {scopeName}
          <IconButton label="Show the whole journal" icon="x" iconSize={12} variant="plain" size={26} className="ml-auto" onClick={() => setScope(null)} />
        </div>
      )}
      {composerFirst && composer}
      {journal.isPending && <p className="text-[13px] text-ink-mute">Loading…</p>}
      {journal.isError && entries.length === 0 && <Alert>{errorMessage(journal.error, 'Could not load the journal.')}</Alert>}
      {journal.isSuccess && entries.length === 0 && (
        <p className="text-[13px] leading-relaxed text-ink-mute">
          {scopeName
            ? `Nothing written about ${scopeName} yet.`
            : 'Nothing written yet. This is where what happened goes — when something went in, what came up, what the frost got. Next year you get to read it back.'}
        </p>
      )}
      {entries.map((e) => (
        <Entry
          key={e.id}
          entry={e}
          gardenId={gardenId}
          objects={objects}
          plantings={plantings}
          canDelete={canEdit && (e.authorId === currentUserId || isOwner)}
          canRewrite={canEdit && e.authorId === currentUserId}
          large={large}
        />
      ))}
      {journal.hasNextPage && (
        <Button variant="ghost" className="self-start text-[13px]" disabled={journal.isFetchingNextPage} onClick={() => void journal.fetchNextPage()}>
          {journal.isFetchingNextPage ? 'Loading…' : 'Older notes'}
        </Button>
      )}
      {!composerFirst && <div className="mt-auto pt-1.5">{composer}</div>}
    </div>
  )
}

function Composer({ gardenId, attach, large }: { gardenId: number; attach: JournalAttach; large: boolean }) {
  const create = useCreateJournalEntry(gardenId)
  const [body, setBody] = useState('')
  const [error, setError] = useState<string | null>(null)

  const submit = () => {
    const text = body.trim()
    if (!text || create.isPending) return
    setError(null)
    create.mutate(
      { body: text, observedAt: today(), objectId: attach.objectId, plantingId: attach.plantingId },
      { onSuccess: () => setBody(''), onError: (err) => setError(errorMessage(err, "Couldn't save that note.")) },
    )
  }
  const onKey = (e: KeyboardEvent<HTMLInputElement>) => {
    if (e.key === 'Enter') {
      e.preventDefault()
      submit()
    }
  }

  return (
    <div className="flex flex-col gap-1.5">
      <div className="flex gap-1.5">
        <input
          className={cn('input', large && 'input-lg')}
          placeholder={attach.label ? `Note something about ${attach.label}…` : 'Note something…'}
          aria-label="New journal note"
          value={body}
          onChange={(e) => setBody(e.target.value)}
          onKeyDown={onKey}
        />
        <IconButton label="Log it" icon="plus" iconSize={large ? 16 : 15} variant="primary" size={large ? 44 : 36} disabled={create.isPending || !body.trim()} onClick={submit} />
      </div>
      {error && <Alert>{error}</Alert>}
    </div>
  )
}

function Entry({
  entry,
  gardenId,
  objects,
  plantings,
  canDelete,
  canRewrite,
  large,
}: {
  entry: JournalEntry
  gardenId: number
  objects: EditorObject[]
  plantings: EditorPlanting[]
  canDelete: boolean
  canRewrite: boolean
  large: boolean
}) {
  const update = useUpdateJournalEntry(gardenId)
  const del = useDeleteJournalEntry(gardenId)
  const [editing, setEditing] = useState(false)
  const [draft, setDraft] = useState(entry.body)
  const [observedAt, setObservedAt] = useState(entry.observedAt)
  const [error, setError] = useState<string | null>(null)

  // A plop-level note names its bed; an object-level note names the object.
  const objectId = entry.objectId ?? (entry.plantingId != null ? plantings.find((p) => p.id === entry.plantingId)?.objectId : undefined)
  const about = objects.find((o) => o.id === objectId)
  const aboutLabel = about ? objectDisplayName(about) + (entry.plantingId != null ? ' · planting' : '') : entry.plantingId != null ? 'a planting' : 'Garden'

  const save = () => {
    const text = draft.trim()
    if (!text) return setError("An entry needs some text — delete it instead if that's what you meant.")
    setError(null)
    update.mutate(
      { id: entry.id, version: entry.version, body: text, observedAt },
      { onSuccess: () => setEditing(false), onError: (err) => setError(errorMessage(err, "Couldn't save that edit.")) },
    )
  }

  return (
    <div className="rounded-md border border-divider bg-bg px-3.5 py-3">
      <div className="mb-[5px] flex items-center gap-1.5">
        <span className="text-[11.5px] font-bold text-accent-2-700">{aboutLabel}</span>
        <span className="ml-auto text-[11.5px] text-ink-mute" title={`by ${entry.authorName}`}>
          {formatObservedAt(entry.observedAt)}
        </span>
      </div>
      {editing ? (
        <div className="flex flex-col gap-2">
          <TextAreaField label="Note" name={`entry-${entry.id}`} rows={3} className={cn(large && 'text-base')} value={draft} onChange={(e) => setDraft(e.target.value)} />
          <TextField label="Observed on" name={`observed-${entry.id}`} type="date" value={observedAt} onChange={(e) => setObservedAt(e.target.value)} />
          <div className="flex justify-end gap-1">
            <Button
              variant="ghost"
              className="px-2.5 text-[13px]"
              onClick={() => {
                setDraft(entry.body)
                setObservedAt(entry.observedAt)
                setEditing(false)
                setError(null)
              }}
            >
              Cancel
            </Button>
            <Button variant="primary" className="px-3.5 text-[13px]" disabled={update.isPending} onClick={save}>
              {update.isPending ? 'Saving…' : 'Save'}
            </Button>
          </div>
        </div>
      ) : (
        <div className={cn('whitespace-pre-wrap leading-relaxed', large ? 'text-[13.5px]' : 'text-[13px]')}>{entry.body}</div>
      )}
      {(canRewrite || canDelete) && !editing && (
        <div className="-mb-1 mt-1 flex justify-end gap-0.5">
          {canRewrite && (
            <button type="button" className="btn btn-ghost px-2 py-0.5 text-[11.5px]" onClick={() => setEditing(true)}>
              Edit
            </button>
          )}
          {canDelete && (
            <button
              type="button"
              className="btn btn-ghost px-2 py-0.5 text-[11.5px] text-accent-700"
              disabled={del.isPending}
              onClick={() => {
                setError(null)
                del.mutate(entry.id, { onError: (err) => setError(errorMessage(err, "Couldn't delete that note.")) })
              }}
            >
              Delete
            </button>
          )}
        </div>
      )}
      {error && <p className="mt-1 text-xs font-semibold text-accent-700">{error}</p>}
    </div>
  )
}
