import { Card } from '@/components/ui/card'
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle
} from '@/components/ui/dialog'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Textarea } from '@/components/ui/textarea'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { ScrollArea } from '@/components/ui/scroll-area'
import Content from '@/components/Content'
import ContentPreview from '@/components/ContentPreview'
import Highlight from '@/components/Note/Highlight'
import MarkdownArticle from '@/components/Note/MarkdownArticle/MarkdownArticle'
import AsciidocArticle from '@/components/Note/AsciidocArticle/AsciidocArticle'
import ClientTag from '@/components/ClientTag'
import {
  ExtendedKind,
  MAX_SIGNED_CUSTOM_EVENT_KIND,
  UNSIGNED_EXPERIMENTAL_KIND_MAX,
  UNSIGNED_EXPERIMENTAL_KIND_MIN,
  isUnsignedExperimentalKind
} from '@/constants'
import { applyImwaldAttributionTags } from '@/lib/draft-event'
import { createFakeEvent } from '@/lib/event'
import logger from '@/lib/logger'
import {
  showPublishingError,
  showPublishingFeedback,
  showSimplePublishSuccess
} from '@/lib/publishing-feedback'
import { cn } from '@/lib/utils'
import { useNostr } from '@/providers/NostrProvider'
import storage from '@/services/local-storage.service'
import type { TDraftEvent } from '@/types'
import dayjs from 'dayjs'
import { AlertTriangle, Plus, Trash2 } from 'lucide-react'
import { Event, kinds } from 'nostr-tools'
import { useCallback, useEffect, useMemo, useRef, useState, type ReactNode } from 'react'
import { useTranslation } from 'react-i18next'

function normalizeTagRow(row: string[]): string[] | null {
  const trimmed = row.map((c) => c.trim())
  if (!trimmed[0]) return null
  let end = trimmed.length
  while (end > 1 && trimmed[end - 1] === '') end--
  return trimmed.slice(0, end)
}

function tagsFromRows(rows: string[][]): string[][] {
  const out: string[][] = []
  for (const row of rows) {
    const n = normalizeTagRow(row)
    if (n) out.push(n)
  }
  return out
}

/** Integer kind in [0, MAX_SIGNED_CUSTOM_EVENT_KIND] or unsigned experimental range; null if invalid / empty. */
function parseEventKindInput(s: string): number | null {
  const trimmed = s.trim()
  if (trimmed === '') return null
  const n = Number(trimmed)
  if (!Number.isInteger(n) || n < 0) return null
  if (n <= MAX_SIGNED_CUSTOM_EVENT_KIND) return n
  if (isUnsignedExperimentalKind(n)) return n
  return null
}

function StaticEventPreview({ event, className }: { event: Event; className?: string }) {
  const k = event.kind
  const wrap = (node: ReactNode) => (
    <Card className={cn('p-3 select-text', className)}>{node}</Card>
  )
  if (k === ExtendedKind.POLL) {
    return wrap(<ContentPreview event={event} />)
  }
  if (k === kinds.Highlights) {
    return wrap(<Highlight event={event} />)
  }
  if (
    k === kinds.ShortTextNote ||
    k === ExtendedKind.COMMENT ||
    k === ExtendedKind.VOICE_COMMENT
  ) {
    return wrap(<MarkdownArticle event={event} hideMetadata />)
  }
  if (k === kinds.LongFormArticle) {
    return wrap(<MarkdownArticle event={event} hideMetadata />)
  }
  if (k === ExtendedKind.WIKI_ARTICLE) {
    return wrap(<AsciidocArticle event={event} hideImagesAndInfo={false} />)
  }
  if (k === ExtendedKind.WIKI_ARTICLE_MARKDOWN) {
    return wrap(<MarkdownArticle event={event} hideMetadata />)
  }
  if (k === ExtendedKind.PUBLICATION_CONTENT) {
    return wrap(<AsciidocArticle event={event} hideImagesAndInfo={false} />)
  }
  return wrap(<Content event={event} className="h-full" mustLoadMedia />)
}

export type TEditOrCloneMode = 'edit' | 'clone'

export type EditOrCloneEventDialogProps =
  | {
      open: boolean
      onOpenChange: (open: boolean) => void
      mode: 'create'
    }
  | {
      open: boolean
      onOpenChange: (open: boolean) => void
      mode: TEditOrCloneMode
      sourceEvent: Event
    }

export default function EditOrCloneEventDialog(props: EditOrCloneEventDialogProps) {
  const { open, onOpenChange, mode } = props
  const isCreate = mode === 'create'
  const sourceEvent = !isCreate ? props.sourceEvent : null

  const { t } = useTranslation()
  const { pubkey, publish, checkLogin } = useNostr()
  const [content, setContent] = useState(() => sourceEvent?.content ?? '')
  const [createKindInput, setCreateKindInput] = useState('1')
  const [tagRows, setTagRows] = useState<string[][]>([['', '']])
  const [activeTab, setActiveTab] = useState('edit')
  const [publishing, setPublishing] = useState(false)
  const prevOpenRef = useRef(false)

  const parsedCreateKind = useMemo(
    () => (isCreate ? parseEventKindInput(createKindInput) : null),
    [isCreate, createKindInput]
  )

  const kind = isCreate ? (parsedCreateKind ?? 0) : sourceEvent!.kind

  useEffect(() => {
    if (open && !prevOpenRef.current) {
      if (isCreate) {
        setCreateKindInput('1')
        setContent('')
        setTagRows([['', '']])
      } else if (sourceEvent) {
        setContent(sourceEvent.content)
        setTagRows(
          sourceEvent.tags?.length
            ? sourceEvent.tags.map((row) => [...row])
            : [['', '']]
        )
      }
      setActiveTab('edit')
    }
    prevOpenRef.current = open
  }, [open, isCreate, sourceEvent])

  const normalizedTags = useMemo(() => tagsFromRows(tagRows), [tagRows])

  const previewEvent = useMemo(() => {
    if (isCreate && parsedCreateKind === null) return null
    const k = isCreate ? parsedCreateKind! : sourceEvent!.kind
    const now = Math.floor(Date.now() / 1000)
    const base: TDraftEvent = {
      kind: k,
      content,
      tags: normalizedTags,
      created_at: now
    }
    const withAttribution = applyImwaldAttributionTags(base, {
      addClientTag: storage.getAddClientTag()
    })
    return createFakeEvent({
      kind: k,
      content,
      tags: withAttribution.tags,
      pubkey: pubkey ?? '',
      created_at: now
    })
  }, [isCreate, parsedCreateKind, sourceEvent, content, normalizedTags, pubkey])

  const buildDraftJson = useCallback(() => {
    if (isCreate && parsedCreateKind === null) {
      return t(
        'Enter a valid event kind: integer 0–{{maxSigned}}, or {{unsignedMin}}–{{unsignedMax}} (unsigned experiment).',
        {
          maxSigned: MAX_SIGNED_CUSTOM_EVENT_KIND,
          unsignedMin: UNSIGNED_EXPERIMENTAL_KIND_MIN,
          unsignedMax: UNSIGNED_EXPERIMENTAL_KIND_MAX
        }
      )
    }
    const k = isCreate ? parsedCreateKind! : sourceEvent!.kind
    const base: TDraftEvent = {
      kind: k,
      content,
      tags: normalizedTags,
      created_at: dayjs().unix()
    }
    const withAttribution = applyImwaldAttributionTags(base, {
      addClientTag: storage.getAddClientTag()
    })
    const unsignedNote = isUnsignedExperimentalKind(withAttribution.kind)
      ? t(
          'Unsigned experimental kind: `sig` will be empty at publish; `id` is still the standard event hash. Not accepted by normal relays. Relays that allow this should authenticate you (e.g. NIP-42 AUTH) before writes.'
        )
      : t('id and sig are assigned when you publish')
    const draft = {
      pubkey: pubkey ?? t('Log in to publish'),
      kind: withAttribution.kind,
      content: withAttribution.content,
      tags: withAttribution.tags,
      created_at: t('Set when you publish'),
      _note: unsignedNote
    }
    return JSON.stringify(draft, null, 2)
  }, [isCreate, parsedCreateKind, sourceEvent, pubkey, content, normalizedTags, t])

  const draftJson = activeTab === 'json' ? buildDraftJson() : ''

  const updateRow = (i: number, j: number, value: string) => {
    setTagRows((rows) => {
      const next = rows.map((r) => [...r])
      if (!next[i]) return rows
      next[i][j] = value
      return next
    })
  }

  const addRow = () => setTagRows((rows) => [...rows, ['', '']])

  const removeRow = (i: number) => {
    setTagRows((rows) => (rows.length <= 1 ? [['', '']] : rows.filter((_, idx) => idx !== i)))
  }

  const addCell = (i: number) => {
    setTagRows((rows) => {
      const next = rows.map((r) => [...r])
      next[i] = [...next[i], '']
      return next
    })
  }

  const removeCell = (i: number, j: number) => {
    setTagRows((rows) => {
      const next = rows.map((r) => [...r])
      if (next[i].length <= 1) return rows
      next[i] = next[i].filter((_, idx) => idx !== j)
      return next
    })
  }

  const handlePublish = async () => {
    await checkLogin(async () => {
      if (!pubkey) return
      if (isCreate) {
        const k = parseEventKindInput(createKindInput)
        if (k === null) {
          showPublishingError(
            t(
              'Kind must be an integer from 0 to {{maxSigned}}, or from {{unsignedMin}} to {{unsignedMax}} (unsigned experiment).',
              {
                maxSigned: MAX_SIGNED_CUSTOM_EVENT_KIND,
                unsignedMin: UNSIGNED_EXPERIMENTAL_KIND_MIN,
                unsignedMax: UNSIGNED_EXPERIMENTAL_KIND_MAX
              }
            )
          )
          return
        }
      }
      setPublishing(true)
      try {
        const publishKind = isCreate ? parseEventKindInput(createKindInput)! : sourceEvent!.kind
        const draft = {
          kind: publishKind,
          content,
          tags: normalizedTags,
          created_at: dayjs().unix()
        }
        const newEvent = await publish(draft, {
          addClientTag: storage.getAddClientTag()
        })
        if ((newEvent as any)?.relayStatuses) {
          const rs = (newEvent as any).relayStatuses
          showPublishingFeedback(
            {
              success: true,
              relayStatuses: rs,
              successCount: rs.filter((s: any) => s.success).length,
              totalCount: rs.length
            },
            { message: t('Post published'), duration: 6000 }
          )
        } else {
          showSimplePublishSuccess(t('Post published'))
        }
        onOpenChange(false)
      } catch (e) {
        if (e instanceof AggregateError && (e as any).relayStatuses) {
          const relayStatuses = (e as any).relayStatuses
          const successCount = relayStatuses.filter((s: any) => s.success).length
          const totalCount = relayStatuses.length
          showPublishingFeedback(
            {
              success: successCount > 0,
              relayStatuses,
              successCount,
              totalCount
            },
            {
              message:
                successCount > 0 ? t('Published to some relays only') : t('Failed to post'),
              duration: 6000
            }
          )
          if (successCount > 0) onOpenChange(false)
        } else {
          logger.error('Edit/clone publish failed', { error: e })
          showPublishingError(e instanceof Error ? e : String(e))
        }
      } finally {
        setPublishing(false)
      }
    })
  }

  const title =
    mode === 'edit'
      ? t('Edit this event')
      : mode === 'clone'
        ? t('Clone or fork this event')
        : t('Create custom event')

  return (
    <Dialog open={open} onOpenChange={onOpenChange}>
      <DialogContent className="max-h-[90vh] w-[95vw] max-w-3xl flex flex-col gap-0 p-0 overflow-hidden">
        <DialogHeader className="shrink-0 px-6 pt-6 pb-2 pr-14">
          <DialogTitle>{title}</DialogTitle>
          <DialogDescription className="sr-only">
            {isCreate
              ? t('Set kind, content, and tags, then publish.')
              : t('Edit content and tags, then publish a new signed event.')}
          </DialogDescription>
        </DialogHeader>

        <div className="flex-1 min-h-0 flex flex-col px-6">
          <Tabs value={activeTab} onValueChange={setActiveTab} className="flex flex-col flex-1 min-h-0 gap-2">
            <TabsList className="w-auto justify-start shrink-0">
              <TabsTrigger value="edit">{t('Edit')}</TabsTrigger>
              <TabsTrigger value="preview">{t('Preview')}</TabsTrigger>
              <TabsTrigger value="json">{t('Json')}</TabsTrigger>
            </TabsList>

            <TabsContent value="edit" className="flex-1 min-h-0 mt-0 data-[state=inactive]:hidden">
              <ScrollArea className="h-[min(50vh,420px)] pr-3">
                <div className="space-y-4 pb-2">
                  <div className="space-y-1">
                    <label className="text-sm font-medium">{t('Event kind')}</label>
                    {isCreate ? (
                      <>
                        <Input
                          type="number"
                          min={0}
                          max={UNSIGNED_EXPERIMENTAL_KIND_MAX}
                          step={1}
                          value={createKindInput}
                          onChange={(e) => setCreateKindInput(e.target.value)}
                          className="font-mono text-sm"
                        />
                        <p className="text-xs text-muted-foreground">
                          {t(
                            'Signed: 0–{{maxSigned}}. Unsigned experiment (empty sig): {{unsignedMin}}–{{unsignedMax}}.',
                            {
                              maxSigned: MAX_SIGNED_CUSTOM_EVENT_KIND,
                              unsignedMin: UNSIGNED_EXPERIMENTAL_KIND_MIN,
                              unsignedMax: UNSIGNED_EXPERIMENTAL_KIND_MAX
                            }
                          )}
                        </p>
                        {parsedCreateKind !== null && isUnsignedExperimentalKind(parsedCreateKind) ? (
                          <div
                            role="alert"
                            className="flex gap-2 rounded-md border border-amber-500/50 bg-amber-500/10 p-3 text-sm text-amber-950 dark:text-amber-100"
                          >
                            <AlertTriangle
                              className="h-5 w-5 shrink-0 text-amber-600 dark:text-amber-400"
                              aria-hidden
                            />
                            <div>
                              <p className="font-medium">{t('Unsigned experimental kind')}</p>
                              <p className="mt-1 text-xs leading-relaxed opacity-90">
                                {t(
                                  'This kind is published with an empty signature. Normal Nostr relays will reject it, and these events are not portable on the open network. Only use relays that explicitly support this experiment and authenticate you (for example with NIP-42 AUTH) before accepting writes.'
                                )}
                              </p>
                            </div>
                          </div>
                        ) : null}
                      </>
                    ) : (
                      <Input
                        type="number"
                        value={kind}
                        disabled
                        readOnly
                        className="font-mono text-sm"
                        aria-readonly
                      />
                    )}
                  </div>
                  <div className="space-y-1">
                    <label className="text-sm font-medium">{t('Note content')}</label>
                    <Textarea
                      value={content}
                      onChange={(e) => setContent(e.target.value)}
                      rows={10}
                      className="font-mono text-sm min-h-[160px]"
                    />
                  </div>
                  <div className="space-y-2">
                    <div className="text-sm font-medium">{t('Tags')}</div>
                    <div className="space-y-2">
                      {tagRows.map((row, i) => (
                        <div
                          key={i}
                          className="flex flex-wrap items-start gap-1 border rounded-md p-2 bg-muted/30"
                        >
                          {row.map((cell, j) => (
                            <div key={j} className="flex items-center gap-0.5 shrink-0">
                              <Input
                                value={cell}
                                onChange={(e) => updateRow(i, j, e.target.value)}
                                placeholder={j === 0 ? t('Tag name') : t('Value')}
                                className="h-8 w-[7rem] sm:w-32 font-mono text-xs"
                              />
                              {row.length > 1 && (
                                <Button
                                  type="button"
                                  variant="ghost"
                                  size="icon"
                                  className="h-8 w-8 shrink-0"
                                  onClick={() => removeCell(i, j)}
                                  aria-label={t('Remove value')}
                                >
                                  <Trash2 className="h-3.5 w-3.5" />
                                </Button>
                              )}
                            </div>
                          ))}
                          <Button
                            type="button"
                            variant="outline"
                            size="sm"
                            className="h-8"
                            onClick={() => addCell(i)}
                          >
                            <Plus className="h-3.5 w-3.5 mr-1" />
                            {t('Add field')}
                          </Button>
                          <Button
                            type="button"
                            variant="ghost"
                            size="sm"
                            className="h-8 ml-auto"
                            onClick={() => removeRow(i)}
                            aria-label={t('Remove tag')}
                          >
                            <Trash2 className="h-4 w-4" />
                          </Button>
                        </div>
                      ))}
                    </div>
                    <Button type="button" variant="secondary" size="sm" onClick={addRow}>
                      <Plus className="h-4 w-4 mr-1" />
                      {t('Add tag')}
                    </Button>
                  </div>
                </div>
              </ScrollArea>
            </TabsContent>

            <TabsContent value="preview" className="flex-1 min-h-0 mt-0 data-[state=inactive]:hidden">
              <ScrollArea className="h-[min(50vh,420px)] pr-3">
                <div className="space-y-1.5">
                  {previewEvent ? (
                    <>
                      {storage.getAddClientTag() ? (
                        <div className="flex min-h-[1.125rem] items-center px-0.5">
                          <ClientTag event={previewEvent} />
                        </div>
                      ) : null}
                      <StaticEventPreview event={previewEvent} />
                    </>
                  ) : (
                    <p className="text-sm text-muted-foreground">
                      {t(
                        'Enter a valid event kind: 0–{{maxSigned}}, or {{unsignedMin}}–{{unsignedMax}}.',
                        {
                          maxSigned: MAX_SIGNED_CUSTOM_EVENT_KIND,
                          unsignedMin: UNSIGNED_EXPERIMENTAL_KIND_MIN,
                          unsignedMax: UNSIGNED_EXPERIMENTAL_KIND_MAX
                        }
                      )}
                    </p>
                  )}
                </div>
              </ScrollArea>
            </TabsContent>

            <TabsContent value="json" className="flex-1 min-h-0 mt-0 data-[state=inactive]:hidden">
              <ScrollArea className="h-[min(50vh,420px)] pr-3">
                <pre className="text-xs font-mono whitespace-pre-wrap break-words border rounded-md p-3 bg-muted/40 select-text">
                  {draftJson}
                </pre>
              </ScrollArea>
            </TabsContent>
          </Tabs>
        </div>

        <DialogFooter className="shrink-0 px-6 py-4 border-t gap-2 sm:gap-0">
          <Button type="button" variant="outline" onClick={() => onOpenChange(false)}>
            {t('Cancel')}
          </Button>
          <Button
            type="button"
            onClick={handlePublish}
            disabled={publishing || !pubkey || (isCreate && parsedCreateKind === null)}
          >
            {publishing ? t('Loading...') : t('Publish')}
          </Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  )
}
