import { useCallback, useEffect, useRef, useState } from 'react'
import { X } from 'lucide-react'
import { getPastedImageFiles, readClipboardImages } from '../lib/imagePaste'

export interface PastedImage {
  mediaType: string
  /** raw base64 (no `data:...;base64,` prefix) — what we send to claude */
  base64: string
  /** `data:...;base64,...` form for `<img src>` */
  previewDataUrl: string
}

interface PastedImageWithId extends PastedImage {
  id: string
  /** Monotonically increasing index used for the `[Image #N]` token. */
  number: number
}

interface PastedText {
  id: string
  text: string
  lines: number
}

const PASTE_LINE_THRESHOLD = 5

/** Mirror of the extension-side ClaudeProfile in src/cc/profiles.ts. */
export interface ClaudeProfile {
  name: string
  path: string
}

interface Props {
  open: boolean
  onCancel: () => void
  onSubmit: (userRequest: string, images: PastedImage[], profilePath?: string) => void
  profiles: ClaudeProfile[]
  defaultProfileName?: string
}

export function NewIssueModal({ open, onCancel, onSubmit, profiles, defaultProfileName }: Props) {
  const [value, setValue] = useState('')
  const [images, setImages] = useState<PastedImageWithId[]>([])
  const [pastedTexts, setPastedTexts] = useState<PastedText[]>([])
  const [nextImageNum, setNextImageNum] = useState(1)
  const [selectedProfile, setSelectedProfile] = useState<string | null>(null)
  const textareaRef = useRef<HTMLTextAreaElement>(null)

  useEffect(() => {
    if (!open) {
      setValue('')
      setImages([])
      setPastedTexts([])
      setNextImageNum(1)
      setSelectedProfile(null)
      return
    }
    // Pick default profile when the modal opens. Prefer the one matching
    // `defaultProfileName`, otherwise fall back to the first alphabetically.
    if (profiles.length > 0) {
      const match = defaultProfileName
        ? profiles.find(p => p.name === defaultProfileName)
        : undefined
      setSelectedProfile(match ? match.name : profiles[0].name)
    }
    else {
      setSelectedProfile(null)
    }
    const t = setTimeout(() => textareaRef.current?.focus(), 0)
    function handleKey(e: KeyboardEvent): void {
      if (e.key === 'Escape')
        onCancel()
    }
    document.addEventListener('keydown', handleKey)
    return () => {
      clearTimeout(t)
      document.removeEventListener('keydown', handleKey)
    }
  }, [open, onCancel, profiles, defaultProfileName])

  const handlePaste = useCallback(async (e: React.ClipboardEvent<HTMLTextAreaElement>) => {
    const files = getPastedImageFiles(e)
    if (files.length > 0) {
      // Intercept so the file binary doesn't end up pasted as garbled text.
      e.preventDefault()
      const ok = await readClipboardImages(files)
      if (ok.length === 0)
        return
      // Assign sequential numbers; mirror pastedTexts and insert `[Image #N]`
      // tokens at the current caret so claude sees where each image belongs.
      let n = nextImageNum
      const enriched: PastedImageWithId[] = ok.map((img) => {
        const num = n
        n += 1
        return {
          ...img,
          id: `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
          number: num,
        }
      })
      setNextImageNum(n)
      setImages(prev => [...prev, ...enriched])
      const tokens = enriched.map(img => `[Image #${img.number}]`).join('')
      const ta = textareaRef.current
      const start = ta?.selectionStart ?? value.length
      const end = ta?.selectionEnd ?? value.length
      const newVal = value.slice(0, start) + tokens + value.slice(end)
      setValue(newVal)
      requestAnimationFrame(() => {
        if (textareaRef.current) {
          textareaRef.current.selectionStart = textareaRef.current.selectionEnd = start + tokens.length
          textareaRef.current.focus()
        }
      })
      return
    }
    // No images — check for large text paste.
    const text = e.clipboardData?.getData('text') ?? ''
    if (!text)
      return
    const lines = text.split('\n').length
    if (lines > PASTE_LINE_THRESHOLD) {
      e.preventDefault()
      const ta = textareaRef.current
      const start = ta?.selectionStart ?? value.length
      const end = ta?.selectionEnd ?? value.length
      const token = `[复制的 ${lines} 行文本]`
      const newVal = value.slice(0, start) + token + value.slice(end)
      setValue(newVal)
      setPastedTexts(prev => [...prev, {
        id: `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
        text,
        lines,
      }])
      requestAnimationFrame(() => {
        if (textareaRef.current) {
          textareaRef.current.selectionStart = textareaRef.current.selectionEnd = start + token.length
          textareaRef.current.focus()
        }
      })
    }
  }, [value, nextImageNum])

  function handleKeyDown(e: React.KeyboardEvent<HTMLTextAreaElement>): void {
    if (e.key !== 'Backspace' && e.key !== 'Delete')
      return
    const ta = textareaRef.current
    if (!ta)
      return
    // Only handle when there's no selection (caret-only)
    if (ta.selectionStart !== ta.selectionEnd)
      return

    const pos = ta.selectionStart
    const text = ta.value

    function tryAtomicDelete(
      tokenRegex: RegExp,
      onMatch?: (match: RegExpExecArray) => void,
    ): boolean {
      let match: RegExpExecArray | null = null
      // eslint-disable-next-line no-cond-assign
      while ((match = tokenRegex.exec(text)) !== null) {
        const tokenStart = match.index
        const tokenEnd = tokenStart + match[0].length
        const hitBackspace = e.key === 'Backspace' && pos > tokenStart && pos <= tokenEnd
        const hitDelete = e.key === 'Delete' && pos >= tokenStart && pos < tokenEnd
        if (!hitBackspace && !hitDelete)
          continue
        e.preventDefault()
        const next = text.slice(0, tokenStart) + text.slice(tokenEnd)
        setValue(next)
        onMatch?.(match)
        requestAnimationFrame(() => {
          if (textareaRef.current) {
            textareaRef.current.selectionStart = textareaRef.current.selectionEnd = tokenStart
            textareaRef.current.focus()
          }
        })
        return true
      }
      return false
    }

    if (tryAtomicDelete(/\[复制的 \d+ 行文本\]/g))
      return
    tryAtomicDelete(/\[Image #(\d+)\]/g, (m) => {
      const num = Number.parseInt(m[1], 10)
      setImages(prev => prev.filter(i => i.number !== num))
    })
  }

  function removeImage(id: string): void {
    const target = images.find(i => i.id === id)
    setImages(prev => prev.filter(i => i.id !== id))
    if (target) {
      const tok = `[Image #${target.number}]`
      setValue(prev => prev.split(tok).join(''))
    }
  }

  if (!open)
    return null

  const trimmed = value.trim()
  const canSubmit = trimmed.length > 0 || images.length > 0 || pastedTexts.length > 0

  function handleSubmit(): void {
    if (!canSubmit)
      return
    const profilePath = selectedProfile
      ? profiles.find(p => p.name === selectedProfile)?.path
      : undefined
    const pending = [...pastedTexts]
    const tokenRegex = /\[复制的 (\d+) 行文本\]/g
    const finalRequest = trimmed.replace(tokenRegex, (_match, linesStr) => {
      const lines = Number.parseInt(linesStr, 10)
      const idx = pending.findIndex(p => p.lines === lines)
      if (idx < 0)
        return ''
      const [taken] = pending.splice(idx, 1)
      return taken.text
    })
    onSubmit(
      finalRequest,
      images.map(({ mediaType, base64, previewDataUrl }) => ({ mediaType, base64, previewDataUrl })),
      profilePath,
    )
  }

  return (
    <div className="fixed inset-0 z-40 flex items-center justify-center bg-black/40">
      <div
        role="dialog"
        aria-modal="true"
        className="w-full max-w-xl rounded-md border border-[var(--vscode-panel-border)] bg-[var(--vscode-editor-background)] p-4 shadow-xl"
      >
        <h2 className="mb-3 text-base font-medium">新建工单</h2>
        <textarea
          ref={textareaRef}
          value={value}
          onChange={e => setValue(e.target.value)}
          onPaste={handlePaste}
          onKeyDown={handleKeyDown}
          placeholder="用 Markdown 描述你的需求…（可 Ctrl+V 粘贴截图）"
          className="mb-2 h-72 w-full resize-none rounded border border-[var(--vscode-input-border,transparent)] bg-[var(--vscode-input-background)] p-2 font-mono text-xs text-[var(--vscode-input-foreground)] outline-none focus:border-[var(--vscode-focusBorder)]"
        />

        {profiles.length > 0 && (
          <fieldset className="mb-3 border-0 p-0">
            <legend className="mb-1 text-xs opacity-70">配置文件</legend>
            <div className="flex flex-wrap gap-3 text-xs">
              {profiles.map(p => (
                <label key={p.name} className="flex cursor-pointer items-center gap-1">
                  <input
                    type="radio"
                    name="claude-profile"
                    value={p.name}
                    checked={selectedProfile === p.name}
                    onChange={() => setSelectedProfile(p.name)}
                    className="accent-[var(--vscode-focusBorder)]"
                  />
                  <span>{p.name}</span>
                </label>
              ))}
            </div>
          </fieldset>
        )}

        {images.length > 0 && (
          <div className="mb-3 flex flex-wrap gap-2">
            {images.map(img => (
              <div
                key={img.id}
                className="group relative h-16 w-16 overflow-hidden rounded border border-[var(--vscode-panel-border)]"
              >
                <img
                  src={img.previewDataUrl}
                  alt="pasted"
                  className="h-full w-full object-cover"
                />
                <span
                  aria-hidden="true"
                  className="pointer-events-none absolute left-0.5 top-0.5 rounded bg-black/60 px-1 text-[10px] leading-tight text-white/90"
                >
                  #
                  {img.number}
                </span>
                <button
                  type="button"
                  onClick={() => removeImage(img.id)}
                  aria-label="移除"
                  className="absolute right-0.5 top-0.5 grid size-4 place-items-center rounded-full bg-black/60 text-white opacity-0 transition-opacity group-hover:opacity-100"
                >
                  <X className="size-2.5" />
                </button>
              </div>
            ))}
          </div>
        )}

        <div className="flex justify-end gap-2">
          <button
            type="button"
            onClick={onCancel}
            className="rounded border border-[var(--vscode-button-border,transparent)] bg-[var(--vscode-button-secondaryBackground)] px-3 py-1.5 text-xs text-[var(--vscode-button-secondaryForeground)] hover:bg-[var(--vscode-button-secondaryHoverBackground)]"
          >
            取消
          </button>
          <button
            type="button"
            onClick={handleSubmit}
            disabled={!canSubmit}
            className="rounded bg-[var(--vscode-button-background)] px-3 py-1.5 text-xs text-[var(--vscode-button-foreground)] hover:bg-[var(--vscode-button-hoverBackground)] disabled:cursor-not-allowed disabled:opacity-50"
          >
            确认
          </button>
        </div>
      </div>
    </div>
  )
}
