"use client"

import type React from "react"
import { useCallback, useMemo, useState } from "react"
import { MessageSquare, Reply, Trash2 } from "lucide-react"
import { Button } from "@/components/ui/button"
import { cn } from "@/lib/utils"
import type { Tag } from "@/lib/mentions"
import { MentionTextarea, TaggedText } from "./mention-input"

/* ------------------------------------------------------------------ */
/* Data contracts (match the real API shape)                          */
/* ------------------------------------------------------------------ */

export type ReactionSummary = {
  emoji: string
  count: number
  reacted_by_me: boolean
  reactors: string[]
}

export type CommentOut = {
  id: string
  parent_comment_id: string | null
  author_user_id: string
  author_display_name: string
  body: string
  created_at: string // ISO 8601
  // Valgfritt (ADR-063): kun round_message_comment (frittstående runder)
  // har tagging. Org-lagchat/turneringsfeedens tilsvarende CommentOut-
  // former (team-chat.tsx/public-tournament.tsx) har aldri dette feltet.
  tags?: Tag[]
}

export type PostEngagementProps = {
  /** Fully-resolved base path, e.g. "/rounds/{id}/messages/{id}". This
   *  component appends "/reaction" and "/comments". */
  apiBase: string
  // Valgfri (ADR-063): kun satt for frittstående runders "Banter Board"
  // (round-messages.tsx/feed.tsx) -- MentionTextarea sitt @-forslags-søk
  // går mot /rounds/{roundId}/taggable-people. Org-lagchat/turneringsfeed
  // (team-chat.tsx/public-tournament.tsx) har ikke dette konseptet og
  // lar feltet stå utelatt -- komposereren faller da tilbake til en vanlig
  // tekstboks uten @-tagging, ingen regresjon for de flytene.
  roundId?: string
  currentUserId: string | null
  initialReactions: ReactionSummary[]
  initialCommentCount: number
  canModerate: boolean
}

/** Curated, fixed reaction set — exact order, no free picker. */
const REACTION_SET = ["👍", "❤️", "😂", "😮", "😢", "🙏"] as const

/** Cap visual indent growth so deep threads stay readable on mobile. */
const MAX_INDENT_DEPTH = 3

const GENERIC_ERROR = "Noe gikk galt. Prøv igjen."

/* ------------------------------------------------------------------ */
/* Helpers                                                            */
/* ------------------------------------------------------------------ */

/** Parse `{ detail: { code, message } }` error envelope, with fallback. */
async function readError(res: Response): Promise<string> {
  try {
    const data = await res.json()
    const msg = data?.detail?.message
    return typeof msg === "string" && msg.trim() !== "" ? msg : GENERIC_ERROR
  } catch {
    return GENERIC_ERROR
  }
}

/** "Kari, Ola og 3 andre reagerte" — 2026-08-07, brukerens eksplisitte krav
 *  om å faktisk kunne se HVEM som reagerte, ikke bare et antall. */
function formatReactorSummary(names: string[]): string {
  if (names.length === 0) return ""
  if (names.length === 1) return `${names[0]} reagerte`
  if (names.length === 2) return `${names[0]} og ${names[1]} reagerte`
  const rest = names.length - 2
  return `${names[0]}, ${names[1]} og ${rest} ${rest === 1 ? "annen" : "andre"} reagerte`
}

/** Short relative / clock-style timestamp, matching the rest of the app. */
function formatTimestamp(iso: string): string {
  const then = new Date(iso)
  if (Number.isNaN(then.getTime())) return ""
  const now = Date.now()
  const diffMs = now - then.getTime()
  const min = Math.floor(diffMs / 60000)
  if (min < 1) return "nå"
  if (min < 60) return `${min} min`
  const hours = Math.floor(min / 60)
  if (hours < 24) return `${hours} t`
  const days = Math.floor(hours / 24)
  if (days < 7) return `${days} d`
  return then.toLocaleDateString("nb-NO", { day: "numeric", month: "short" })
}

/* ------------------------------------------------------------------ */
/* Main component                                                     */
/* ------------------------------------------------------------------ */

export function PostEngagement({
  apiBase,
  roundId,
  currentUserId,
  initialReactions,
  initialCommentCount,
  canModerate,
}: PostEngagementProps) {
  const loggedIn = currentUserId !== null

  /* -------------------------- Reactions -------------------------- */
  const [reactions, setReactions] = useState<ReactionSummary[]>(initialReactions)
  const [reactionError, setReactionError] = useState<string | null>(null)
  const [reactionPending, setReactionPending] = useState(false)

  // Merge the curated set with server data so all six always render in order,
  // even when a given emoji has no reactions yet.
  const reactionRow = useMemo(() => {
    const byEmoji = new Map(reactions.map((r) => [r.emoji, r]))
    return REACTION_SET.map(
      (emoji) => byEmoji.get(emoji) ?? { emoji, count: 0, reacted_by_me: false, reactors: [] },
    )
  }, [reactions])

  const toggleReaction = useCallback(
    async (r: ReactionSummary) => {
      if (!loggedIn || reactionPending) return
      setReactionPending(true)
      setReactionError(null)
      // Tap your own reaction -> DELETE; otherwise PUT (switching is just a PUT,
      // the backend replaces any prior reaction of yours).
      const method = r.reacted_by_me ? "DELETE" : "PUT"
      try {
        const res = await fetch(`${apiBase}/reaction`, {
          method,
          credentials: "include",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ emoji: r.emoji }),
        })
        if (!res.ok) {
          setReactionError(await readError(res))
          return
        }
        const fresh = (await res.json()) as ReactionSummary[]
        setReactions(fresh)
      } catch {
        setReactionError(GENERIC_ERROR)
      } finally {
        setReactionPending(false)
      }
    },
    [apiBase, loggedIn, reactionPending],
  )

  /* --------------------------- Comments -------------------------- */
  const [expanded, setExpanded] = useState(false)
  const [comments, setComments] = useState<CommentOut[]>([])
  const [fetched, setFetched] = useState(false)
  const [loading, setLoading] = useState(false)
  const [threadError, setThreadError] = useState<string | null>(null)
  const [commentCount, setCommentCount] = useState(initialCommentCount)

  const loadComments = useCallback(async () => {
    setLoading(true)
    setThreadError(null)
    try {
      const res = await fetch(`${apiBase}/comments`, { credentials: "include" })
      if (!res.ok) {
        setThreadError(await readError(res))
        return
      }
      const data = (await res.json()) as CommentOut[]
      setComments(data)
      setFetched(true)
    } catch {
      setThreadError(GENERIC_ERROR)
    } finally {
      setLoading(false)
    }
  }, [apiBase])

  const toggleExpanded = useCallback(() => {
    setExpanded((prev) => {
      const next = !prev
      if (next && !fetched && !loading) void loadComments()
      return next
    })
  }, [fetched, loading, loadComments])

  // Build a nested tree from the flat list, preserving API order (chronological
  // oldest-first). Children are grouped by parent id.
  const { roots, childrenOf } = useMemo(() => {
    const childrenOf = new Map<string, CommentOut[]>()
    const roots: CommentOut[] = []
    for (const c of comments) {
      if (c.parent_comment_id === null) {
        roots.push(c)
      } else {
        const arr = childrenOf.get(c.parent_comment_id) ?? []
        arr.push(c)
        childrenOf.set(c.parent_comment_id, arr)
      }
    }
    return { roots, childrenOf }
  }, [comments])

  const appendComment = useCallback((c: CommentOut) => {
    setComments((prev) => [...prev, c])
    setCommentCount((n) => n + 1)
  }, [])

  const removeSubtree = useCallback((id: string) => {
    setComments((prev) => {
      // Collect the target plus all transitive descendants.
      const toRemove = new Set<string>([id])
      let grew = true
      while (grew) {
        grew = false
        for (const c of prev) {
          if (c.parent_comment_id && toRemove.has(c.parent_comment_id) && !toRemove.has(c.id)) {
            toRemove.add(c.id)
            grew = true
          }
        }
      }
      setCommentCount((n) => Math.max(0, n - toRemove.size))
      return prev.filter((c) => !toRemove.has(c.id))
    })
  }, [])

  /* One reply composer open at a time, tracked by target comment id. */
  const [replyingTo, setReplyingTo] = useState<string | null>(null)

  /* Hvem-reagerte-detalj (2026-08-07) — dataen er allerede i reactionRow
   * (samme batch-henting som selve tellingen), så dette er ren
   * visningslogikk, ingen ekstra fetch. */
  const [showReactors, setShowReactors] = useState(false)
  const activeReactionGroups = reactionRow.filter((r) => r.count > 0)
  const allReactorNames = activeReactionGroups.flatMap((r) => r.reactors)

  return (
    <section className="flex flex-col gap-4 pt-2" aria-label="Reaksjoner og kommentarer">
      {/* ---------------------------- Reactions ---------------------------- */}
      <div className="flex flex-col gap-2">
        <div className="flex flex-wrap gap-1 rounded-full bg-muted p-1" role="group" aria-label="Reaksjoner">
          {reactionRow.map((r) => {
            const active = r.reacted_by_me
            return (
              <button
                key={r.emoji}
                type="button"
                onClick={() => toggleReaction(r)}
                disabled={!loggedIn || reactionPending}
                aria-pressed={active}
                aria-label={`Reager med ${r.emoji}${r.count > 0 ? `, ${r.count}` : ""}`}
                className={cn(
                  "inline-flex min-h-11 items-center justify-center gap-1.5 rounded-full px-3 text-lg leading-none transition-transform focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background",
                  "enabled:hover:brightness-95 enabled:active:scale-[0.98] disabled:cursor-default disabled:opacity-70",
                  active
                    ? "bg-primary text-primary-foreground"
                    : "border border-border bg-card text-foreground",
                )}
              >
                <span aria-hidden="true">{r.emoji}</span>
                {r.count > 0 && (
                  <span
                    className={cn(
                      "text-sm font-bold tabular-nums",
                      active ? "text-primary-foreground" : "text-muted-foreground",
                    )}
                  >
                    {r.count}
                  </span>
                )}
              </button>
            )
          })}
        </div>
        {reactionError && (
          <p role="alert" className="px-1 text-sm font-semibold text-destructive">
            {reactionError}
          </p>
        )}

        {/* Hvem reagerte -- alltid synlig sammendrag (ikke bare hover/tap,
            tilgjengelighetsregelen), utvidbart til full per-emoji-liste. */}
        {allReactorNames.length > 0 && (
          <div className="flex flex-col gap-1 px-1">
            <button
              type="button"
              onClick={() => setShowReactors((v) => !v)}
              aria-expanded={showReactors}
              className="flex min-h-11 w-fit items-center text-left text-sm text-muted-foreground hover:text-foreground hover:underline"
            >
              {formatReactorSummary(allReactorNames)}
            </button>
            {showReactors && (
              <ul className="flex flex-col gap-1">
                {activeReactionGroups.map((r) => (
                  <li key={r.emoji} className="flex items-start gap-2 text-sm text-foreground">
                    <span aria-hidden="true">{r.emoji}</span>
                    <span>{r.reactors.join(", ")}</span>
                  </li>
                ))}
              </ul>
            )}
          </div>
        )}
      </div>

      {/* ------------------------ Expand / count ------------------------ */}
      <div>
        <Button
          type="button"
          variant="ghost"
          onClick={toggleExpanded}
          aria-expanded={expanded}
          className="h-11 gap-2 rounded-xl px-3 text-base font-bold text-foreground hover:bg-accent/50 active:scale-[0.98] focus-visible:ring-2 focus-visible:ring-ring"
        >
          <MessageSquare aria-hidden="true" className="size-5" />
          Kommentarer ({commentCount})
        </Button>
      </div>

      {/* ---------------------------- Thread ---------------------------- */}
      {expanded && (
        <div className="flex flex-col gap-4">
          {loading && (
            <div className="flex justify-center py-6">
              <span
                className="size-8 animate-spin rounded-full border-4 border-primary/20 border-t-primary"
                role="status"
                aria-label="Laster kommentarer"
              />
            </div>
          )}

          {threadError && !loading && (
            <div className="flex flex-col gap-2">
              <p role="alert" className="text-sm font-semibold text-destructive">
                {threadError}
              </p>
              <Button
                type="button"
                variant="outline"
                onClick={() => void loadComments()}
                className="h-11 w-fit rounded-xl text-base font-bold active:scale-[0.98]"
              >
                Prøv igjen
              </Button>
            </div>
          )}

          {!loading && !threadError && fetched && (
            <>
              {roots.length === 0 ? (
                <p className="py-2 text-base text-muted-foreground">Ingen kommentarer ennå.</p>
              ) : (
                <ul className="flex flex-col gap-4">
                  {roots.map((c) => (
                    <CommentNode
                      key={c.id}
                      comment={c}
                      depth={0}
                      childrenOf={childrenOf}
                      apiBase={apiBase}
                      roundId={roundId}
                      currentUserId={currentUserId}
                      canModerate={canModerate}
                      replyingTo={replyingTo}
                      setReplyingTo={setReplyingTo}
                      onAppend={appendComment}
                      onRemoveSubtree={removeSubtree}
                    />
                  ))}
                </ul>
              )}

              {/* Bottom composer (top-level) */}
              {loggedIn ? (
                <CommentComposer
                  apiBase={apiBase}
                  roundId={roundId}
                  parentCommentId={null}
                  onPosted={appendComment}
                  autoFocus={false}
                />
              ) : (
                <p className="text-base text-muted-foreground">Logg inn for å kommentere.</p>
              )}
            </>
          )}
        </div>
      )}
    </section>
  )
}

/* ------------------------------------------------------------------ */
/* Single comment (recursive)                                         */
/* ------------------------------------------------------------------ */

function CommentNode({
  comment,
  depth,
  childrenOf,
  apiBase,
  roundId,
  currentUserId,
  canModerate,
  replyingTo,
  setReplyingTo,
  onAppend,
  onRemoveSubtree,
}: {
  comment: CommentOut
  depth: number
  childrenOf: Map<string, CommentOut[]>
  apiBase: string
  roundId?: string
  currentUserId: string | null
  canModerate: boolean
  replyingTo: string | null
  setReplyingTo: (id: string | null) => void
  onAppend: (c: CommentOut) => void
  onRemoveSubtree: (id: string) => void
}) {
  const loggedIn = currentUserId !== null
  const children = childrenOf.get(comment.id) ?? []
  const hasReplies = children.length > 0
  const canDelete = comment.author_user_id === currentUserId || canModerate

  const [confirming, setConfirming] = useState(false)
  const [deleting, setDeleting] = useState(false)
  const [deleteError, setDeleteError] = useState<string | null>(null)

  const isReplyOpen = replyingTo === comment.id

  const handleDelete = useCallback(async () => {
    setDeleting(true)
    setDeleteError(null)
    try {
      const res = await fetch(`${apiBase}/comments/${comment.id}`, {
        method: "DELETE",
        credentials: "include",
      })
      if (!res.ok) {
        setDeleteError(await readError(res))
        return
      }
      onRemoveSubtree(comment.id)
    } catch {
      setDeleteError(GENERIC_ERROR)
    } finally {
      setDeleting(false)
      setConfirming(false)
    }
  }, [apiBase, comment.id, onRemoveSubtree])

  // Indent per level via a left border, but stop growing past the cap so long
  // threads on mobile don't squeeze content into a sliver.
  const indented = depth > 0
  const cappedForChildren = Math.min(depth, MAX_INDENT_DEPTH)

  return (
    <li
      className={cn(indented && "border-l-2 border-border pl-4")}
    >
      <div className="flex flex-col gap-2">
        {/* Author + timestamp */}
        <div className="flex flex-wrap items-baseline gap-x-2 gap-y-0.5">
          <span className="text-base font-bold text-foreground">{comment.author_display_name}</span>
          <span className="text-sm tabular-nums text-muted-foreground">
            {formatTimestamp(comment.created_at)}
          </span>
        </div>

        {/* Body */}
        <p className="whitespace-pre-wrap break-words text-base leading-relaxed text-foreground">
          <TaggedText body={comment.body} tags={comment.tags ?? []} />
        </p>

        {/* Actions */}
        <div className="flex flex-wrap items-center gap-1">
          {loggedIn && (
            <Button
              type="button"
              variant="ghost"
              onClick={() => setReplyingTo(isReplyOpen ? null : comment.id)}
              aria-expanded={isReplyOpen}
              className="h-11 gap-1.5 rounded-xl px-3 text-sm font-bold text-muted-foreground hover:bg-accent/50 hover:text-foreground active:scale-[0.98] focus-visible:ring-2 focus-visible:ring-ring"
            >
              <Reply aria-hidden="true" className="size-4" />
              Svar
            </Button>
          )}

          {canDelete && !confirming && (
            <Button
              type="button"
              variant="ghost"
              onClick={() => setConfirming(true)}
              className="h-11 gap-1.5 rounded-xl px-3 text-sm font-bold text-destructive hover:bg-destructive/10 hover:text-destructive active:scale-[0.98] focus-visible:ring-2 focus-visible:ring-ring"
            >
              <Trash2 aria-hidden="true" className="size-4" />
              Slett
            </Button>
          )}

          {canDelete && confirming && (
            <div className="flex flex-wrap items-center gap-1">
              <Button
                type="button"
                variant="ghost"
                onClick={handleDelete}
                disabled={deleting}
                className="h-11 gap-1.5 rounded-xl px-3 text-sm font-bold text-destructive hover:bg-destructive/10 hover:text-destructive active:scale-[0.98] focus-visible:ring-2 focus-visible:ring-ring"
              >
                <Trash2 aria-hidden="true" className="size-4" />
                Sikker?
              </Button>
              <Button
                type="button"
                variant="ghost"
                onClick={() => setConfirming(false)}
                disabled={deleting}
                className="h-11 rounded-xl px-3 text-sm font-bold text-foreground hover:bg-accent/50 active:scale-[0.98] focus-visible:ring-2 focus-visible:ring-ring"
              >
                Avbryt
              </Button>
              {hasReplies && (
                <p className="basis-full text-sm text-muted-foreground text-pretty">
                  Sletter du denne, forsvinner også svarene under.
                </p>
              )}
            </div>
          )}
        </div>

        {deleteError && (
          <p role="alert" className="text-sm font-semibold text-destructive">
            {deleteError}
          </p>
        )}

        {/* Inline reply composer */}
        {isReplyOpen && loggedIn && (
          <CommentComposer
            apiBase={apiBase}
            roundId={roundId}
            parentCommentId={comment.id}
            autoFocus
            onPosted={(c) => {
              onAppend(c)
              setReplyingTo(null)
            }}
            onCancel={() => setReplyingTo(null)}
          />
        )}
      </div>

      {/* Children */}
      {hasReplies && (
        <ul className="mt-4 flex flex-col gap-4">
          {children.map((child) => (
            <CommentNode
              key={child.id}
              comment={child}
              depth={cappedForChildren + 1}
              childrenOf={childrenOf}
              apiBase={apiBase}
              roundId={roundId}
              currentUserId={currentUserId}
              canModerate={canModerate}
              replyingTo={replyingTo}
              setReplyingTo={setReplyingTo}
              onAppend={onAppend}
              onRemoveSubtree={onRemoveSubtree}
            />
          ))}
        </ul>
      )}
    </li>
  )
}

/* ------------------------------------------------------------------ */
/* Composer                                                           */
/* ------------------------------------------------------------------ */

function CommentComposer({
  apiBase,
  roundId,
  parentCommentId,
  onPosted,
  onCancel,
  autoFocus,
}: {
  apiBase: string
  roundId?: string
  parentCommentId: string | null
  onPosted: (c: CommentOut) => void
  onCancel?: () => void
  autoFocus?: boolean
}) {
  const [body, setBody] = useState("")
  const [tags, setTags] = useState<Tag[]>([])
  const [submitting, setSubmitting] = useState(false)
  const [error, setError] = useState<string | null>(null)

  const empty = body.trim() === ""

  const submit = useCallback(async () => {
    if (empty || submitting) return
    setSubmitting(true)
    setError(null)
    try {
      const res = await fetch(`${apiBase}/comments`, {
        method: "POST",
        credentials: "include",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          body: body.trim(),
          ...(parentCommentId ? { parent_comment_id: parentCommentId } : {}),
          // Kun med når roundId faktisk er satt (round_message_comment-
          // flyten) -- org-lagchat/turneringsfeedens comment-endepunkt
          // aksepterer ikke (og trenger ikke) dette feltet.
          ...(roundId ? { tags } : {}),
        }),
      })
      if (!res.ok) {
        setError(await readError(res))
        return
      }
      const created = (await res.json()) as CommentOut
      onPosted(created)
      setBody("")
      setTags([])
    } catch {
      setError(GENERIC_ERROR)
    } finally {
      setSubmitting(false)
    }
  }, [apiBase, body, empty, onPosted, parentCommentId, roundId, submitting, tags])

  const composerClassName =
    "min-h-11 w-full resize-y rounded-2xl border border-border bg-card px-4 py-3 text-base leading-relaxed text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background"

  function handleComposerKeyDown(e: React.KeyboardEvent<HTMLTextAreaElement>) {
    // Cmd/Ctrl+Enter submits; plain Enter keeps newlines. Respect IME.
    if ((e.metaKey || e.ctrlKey) && e.key === "Enter" && !e.nativeEvent.isComposing && e.keyCode !== 229) {
      e.preventDefault()
      void submit()
    }
  }

  return (
    <div className="flex flex-col gap-2">
      {roundId ? (
        <MentionTextarea
          roundId={roundId}
          // eslint-disable-next-line jsx-a11y/no-autofocus -- intentional: composer opens on explicit user action
          autoFocus={autoFocus}
          value={body}
          tags={tags}
          onChange={(v, t) => {
            setBody(v)
            setTags(t)
          }}
          onKeyDownWhenClosed={handleComposerKeyDown}
          rows={parentCommentId ? 2 : 3}
          placeholder={parentCommentId ? "Skriv et svar … (@ for å tagge)" : "Skriv en kommentar … (@ for å tagge)"}
          className={composerClassName}
        />
      ) : (
        <textarea
          // eslint-disable-next-line jsx-a11y/no-autofocus -- intentional: composer opens on explicit user action
          autoFocus={autoFocus}
          value={body}
          onChange={(e) => setBody(e.target.value)}
          onKeyDown={handleComposerKeyDown}
          rows={parentCommentId ? 2 : 3}
          placeholder={parentCommentId ? "Skriv et svar …" : "Skriv en kommentar …"}
          className={composerClassName}
        />
      )}
      {error && (
        <p role="alert" className="text-sm font-semibold text-destructive">
          {error}
        </p>
      )}
      <div className="flex items-center gap-2">
        <Button
          type="button"
          onClick={() => void submit()}
          disabled={empty || submitting}
          className="h-11 rounded-xl px-5 text-base font-bold active:scale-[0.98] focus-visible:ring-2 focus-visible:ring-ring"
        >
          {submitting ? "Sender …" : "Send"}
        </Button>
        {onCancel && (
          <Button
            type="button"
            variant="ghost"
            onClick={onCancel}
            disabled={submitting}
            className="h-11 rounded-xl px-4 text-base font-bold text-muted-foreground hover:bg-accent/50 hover:text-foreground active:scale-[0.98] focus-visible:ring-2 focus-visible:ring-ring"
          >
            Avbryt
          </Button>
        )}
      </div>
    </div>
  )
}

export default PostEngagement
