/**
 * FEEDBACK: "Fehler melden" — a small accessible modal with a textarea + submit.
 *
 * The caller passes the debug context (which view, the gerbil/litter id + name); the
 * dialog captures the current URL and a client timestamp itself and POSTs everything
 * to /feedback. Success/error is surfaced via the existing toast system.
 */
import { useEffect, useMemo, useRef, useState } from 'react'
import { Link } from 'react-router-dom'
import { de } from '../strings/de'
import { ApiError } from '../api/client'
import {
  listFeedback,
  readFileAsUpload,
  submitFeedback,
  uploadAttachment,
  type FeedbackContext,
  type FeedbackTicket,
} from '../api/feedback'
import { useToast } from './toast'
import './reportErrorDialog.css'

/** Wörter (≥3 Zeichen) für einen einfachen Ähnlichkeits-Score. */
function wordSet(s: string): Set<string> {
  return new Set((s.toLowerCase().match(/[a-zäöüß0-9]{3,}/g) ?? []))
}

export interface ReportErrorContext {
  context: FeedbackContext
  gerbilId?: string | null
  litterId?: string | null
  contactId?: string | null
  entityName?: string | null
}

interface ReportErrorDialogProps {
  open: boolean
  onClose: () => void
  context: ReportErrorContext
}

/**
 * Thin wrapper: mounts the dialog body only while open, so its form state starts
 * fresh on every open (no reset-in-effect needed).
 */
export default function ReportErrorDialog({ open, onClose, context }: ReportErrorDialogProps) {
  if (!open) return null
  return <ReportErrorDialogBody onClose={onClose} context={context} />
}

function ReportErrorDialogBody({
  onClose,
  context,
}: {
  onClose: () => void
  context: ReportErrorContext
}) {
  const t = de.feedback
  const toast = useToast()
  // EIN gemeinsamer Entwurf fürs Melde-Fenster (nicht pro Kontext): so kommt der Text auch
  // dann zurück, wenn das Fenster (versehentlich) geschlossen wird, man in der App woanders
  // hin navigiert (anderer Kontext) und „Fehler melden" erneut öffnet — oder die Seite am
  // Handy beim App-Wechsel aus dem Speicher geworfen wird. Wird bei jeder Eingabe gesichert
  // und erst nach erfolgreichem Senden gelöscht.
  const draftKey = 'report-error-draft'
  const [message, setMessage] = useState(() => {
    try {
      return localStorage.getItem(draftKey) ?? ''
    } catch {
      return ''
    }
  })
  const [submitting, setSubmitting] = useState(false)
  const [files, setFiles] = useState<File[]>([])
  const textareaRef = useRef<HTMLTextAreaElement | null>(null)

  // Bereits gelöste Tickets einmalig laden, um beim Tippen ähnliche vorzuschlagen.
  const [resolvedTickets, setResolvedTickets] = useState<FeedbackTicket[]>([])
  useEffect(() => {
    let cancelled = false
    listFeedback()
      .then((all) => {
        if (!cancelled)
          setResolvedTickets(all.filter((tk) => tk.status === 'Resolved' && !tk.deletedAt))
      })
      .catch(() => {
        /* ohne Vorschläge weiter */
      })
    return () => {
      cancelled = true
    }
  }, [])

  // Ähnliche gelöste Tickets: Wort-Überlappung + Bonus bei gleichem Tier/Wurf/Kontakt.
  const similar = useMemo(() => {
    const msgWords = wordSet(message)
    if (msgWords.size < 2) return []
    const scored = resolvedTickets.map((tk) => {
      const hay = wordSet(`${tk.message} ${tk.fixNote ?? ''} ${tk.entityName ?? ''}`)
      let score = 0
      for (const w of msgWords) if (hay.has(w)) score++
      if (
        (context.gerbilId && tk.gerbilId === context.gerbilId) ||
        (context.litterId && tk.litterId === context.litterId) ||
        (context.contactId && tk.contactId === context.contactId)
      )
        score += 2
      return { tk, score }
    })
    return scored
      .filter((s) => s.score >= 2)
      .sort((a, b) => b.score - a.score)
      .slice(0, 3)
      .map((s) => s.tk)
  }, [message, resolvedTickets, context.gerbilId, context.litterId, context.contactId])

  // Entwurf laufend sichern (bei jeder Eingabe) bzw. entfernen, wenn leer.
  useEffect(() => {
    try {
      if (message) localStorage.setItem(draftKey, message)
      else localStorage.removeItem(draftKey)
    } catch {
      /* localStorage nicht verfügbar — Entwurf dann nur im Speicher */
    }
  }, [message, draftKey])

  // Focus the textarea once on mount.
  useEffect(() => {
    const id = window.setTimeout(() => textareaRef.current?.focus(), 0)
    return () => window.clearTimeout(id)
  }, [])

  // Close on Escape.
  useEffect(() => {
    const onKey = (e: KeyboardEvent) => {
      if (e.key === 'Escape') onClose()
    }
    window.addEventListener('keydown', onKey)
    return () => window.removeEventListener('keydown', onKey)
  }, [onClose])

  const contextLabel = t.contexts[context.context]

  async function handleSubmit() {
    const trimmed = message.trim()
    if (!trimmed) {
      toast.error(t.emptyMessage)
      textareaRef.current?.focus()
      return
    }
    setSubmitting(true)
    try {
      const created = await submitFeedback({
        message: trimmed,
        context: context.context,
        gerbilId: context.gerbilId ?? null,
        litterId: context.litterId ?? null,
        contactId: context.contactId ?? null,
        entityName: context.entityName ?? null,
        url: window.location.href,
        clientTimestamp: new Date().toISOString(),
      })
      // Ausgewählte Anhänge nach dem Anlegen hochladen (Ticket-ID liegt erst jetzt vor).
      for (const file of files) {
        try {
          await uploadAttachment(created.id, await readFileAsUpload(file))
        } catch {
          toast.error(t.attachmentError)
        }
      }
      try {
        localStorage.removeItem(draftKey)
      } catch {
        /* ignorieren */
      }
      toast.success(t.success)
      onClose()
    } catch (err) {
      toast.error(err instanceof ApiError ? err.message : t.error)
      setSubmitting(false)
    }
  }

  return (
    <div
      className="report-error__backdrop"
      onClick={onClose}
      role="presentation"
    >
      <div
        className="report-error__dialog"
        role="dialog"
        aria-modal="true"
        aria-labelledby="report-error-title"
        onClick={(e) => e.stopPropagation()}
      >
        <div className="report-error__header">
          <h2 id="report-error-title" className="report-error__title">
            {t.dialogTitle}
          </h2>
          <button
            type="button"
            className="report-error__close"
            onClick={onClose}
            aria-label={t.close}
          >
            ✕
          </button>
        </div>

        <p className="report-error__intro">{t.intro}</p>

        <label className="report-error__label" htmlFor="report-error-message">
          {t.label}
        </label>
        <textarea
          id="report-error-message"
          ref={textareaRef}
          className="report-error__textarea"
          value={message}
          onChange={(e) => setMessage(e.target.value)}
          placeholder={t.placeholder}
          rows={5}
        />

        {similar.length > 0 && (
          <div className="report-error__similar">
            <div className="report-error__similar-title">{t.similarTitle}</div>
            <ul className="report-error__similar-list">
              {similar.map((tk) => (
                <li key={tk.id}>
                  <Link to={`/hilfe/tickets?focus=${tk.id}`} onClick={onClose}>
                    {tk.message.length > 80 ? `${tk.message.slice(0, 80)}…` : tk.message}
                  </Link>
                </li>
              ))}
            </ul>
          </div>
        )}

        <div className="report-error__attach">
          <label className="report-error__label" htmlFor="report-error-files">
            {t.attachLabel}
          </label>
          <input
            id="report-error-files"
            type="file"
            accept="image/*"
            multiple
            onChange={(e) => setFiles(Array.from(e.target.files ?? []))}
          />
          {files.length > 0 && (
            <ul className="report-error__attach-list">
              {files.map((f, i) => (
                <li key={i}>{f.name}</li>
              ))}
            </ul>
          )}
        </div>

        <div className="report-error__debug">
          <div className="report-error__debug-title">{t.debugTitle}</div>
          <dl className="report-error__debug-list">
            <div className="report-error__debug-row">
              <dt>{t.debugFields.context}</dt>
              <dd>{contextLabel}</dd>
            </div>
            {context.entityName && (
              <div className="report-error__debug-row">
                <dt>{t.debugFields.entity}</dt>
                <dd>{context.entityName}</dd>
              </div>
            )}
            <div className="report-error__debug-row">
              <dt>{t.debugFields.url}</dt>
              <dd className="report-error__debug-url">{window.location.href}</dd>
            </div>
          </dl>
        </div>

        <div className="report-error__actions">
          <button type="button" className="btn" onClick={onClose} disabled={submitting}>
            {t.cancel}
          </button>
          <button
            type="button"
            className="btn btn--primary"
            onClick={handleSubmit}
            disabled={submitting}
          >
            {submitting ? t.submitting : t.submit}
          </button>
        </div>
      </div>
    </div>
  )
}
