'use client';

/**
 * Inline per-row review actions — the fast-triage cluster for a status table (bd
 * startsim-768w.18.15). Renders the (up to) three decisions from the schema as
 * compact icon buttons ✕ reject · ✓ good · ✎ edit; a click writes status AND
 * team_verdict together (the coherent pair, same as ReviewDrawer) IN PLACE —
 * optimistic, no drawer. The current verdict shows as the active button.
 *
 * The ✎ "edit / needs work" decision opens a small popover with a note box so the
 * reviewer says HOW it should change — saved to the note attr alongside the
 * status+verdict move. Meant to live in a table's trailing "Actions" column; it
 * stops click propagation so acting never triggers the row's drawer-open.
 *
 * Generic + client-injected: pass a CollectionClient + the record; defaults match
 * the OGMC content schema but everything routes through resolveReviewConfig.
 */
import { useMemo, useState } from 'react';
import { Check, X, PencilLine } from 'lucide-react';

import { Button } from '../ui/button';
import { Textarea } from '../ui/textarea';
import { Popover, PopoverContent, PopoverTrigger } from '../ui/popover';
import { notify } from '../toast/use-notify';
import { cn } from '../../lib/utils';
import type { CollectionClient, EntityRecord, EntityTypeDef } from './types';
import { toCamelKey } from './data';
import {
  applyDecisionToData,
  fieldStr,
  resolveReviewConfig,
  reviewDecisions,
  type ReviewConfig,
  type ReviewDecision,
  type VerdictTone,
} from './review';

const ICON: Record<VerdictTone, typeof Check> = { good: Check, bad: X, neutral: PencilLine };
const ACTIVE: Record<VerdictTone, string> = {
  good: 'bg-emerald-600 text-white border-emerald-600',
  bad: 'bg-rose-600 text-white border-rose-600',
  neutral: 'bg-amber-500 text-white border-amber-500',
};
const IDLE: Record<VerdictTone, string> = {
  good: 'border-border bg-card text-emerald-600 hover:bg-emerald-50',
  bad: 'border-border bg-card text-rose-600 hover:bg-rose-50',
  neutral: 'border-border bg-card text-amber-600 hover:bg-amber-50',
};
const BTN = 'flex h-8 w-8 items-center justify-center rounded-md border transition disabled:opacity-50';

// Display order: ✕ reject · ✓ good · ✎ edit — a scan-left-to-right pass/fail/fix.
const ORDER = ['reject', 'approve', 'needs_work'];

export interface InlineReviewActionsProps {
  client: CollectionClient;
  type: EntityTypeDef;
  record: EntityRecord;
  /** Field-map + transition overrides; defaults match the OGMC content schema. */
  config?: ReviewConfig;
  /** Fired after a successful save so the parent can refetch/invalidate. */
  onSaved?: () => void;
}

export function InlineReviewActions({ client, type, record, config, onSaved }: InlineReviewActionsProps) {
  const cfg = useMemo(() => resolveReviewConfig(type, config), [type, config]);
  const decisions = useMemo(
    () => [...reviewDecisions(cfg)].sort((a, b) => ORDER.indexOf(a.key) - ORDER.indexOf(b.key)),
    [cfg],
  );
  // Which decision the record currently reflects — by verdict when the type has
  // one (topics), else by status (news: acceptable→approve, rejected→reject). The
  // matching button shows active. Optimistically updated on click.
  const initialKey = useMemo(() => {
    const v = fieldStr(record.data, cfg.verdictAttr);
    const s = fieldStr(record.data, cfg.statusName);
    return (
      decisions.find((d) => (d.verdict ? d.verdict === v : d.status ? d.status === s : false))?.key ??
      null
    );
  }, [record.data, cfg, decisions]);
  const [appliedKey, setAppliedKey] = useState<string | null>(initialKey);
  const [busy, setBusy] = useState<string | null>(null);
  const [noteOpen, setNoteOpen] = useState(false);
  const [note, setNote] = useState('');

  /** Apply a decision (status + verdict), optionally attaching a note. Optimistic. */
  async function apply(d: ReviewDecision, noteText?: string) {
    const prev = appliedKey;
    setAppliedKey(d.key); // optimistic
    setBusy(d.key);
    try {
      const data = applyDecisionToData(record.data, d, cfg);
      if (noteText !== undefined) {
        const camel = toCamelKey(cfg.noteAttr);
        const t = noteText.trim();
        if (t) {
          data[camel] = t;
          if (camel !== cfg.noteAttr) delete data[cfg.noteAttr];
        } else {
          delete data[camel];
          delete data[cfg.noteAttr];
        }
      }
      await client.updateEntity(record.id, { data });
      onSaved?.();
      return true;
    } catch (err) {
      setAppliedKey(prev); // roll back
      notify.error(err instanceof Error ? err.message : 'Could not save.');
      return false;
    } finally {
      setBusy(null);
    }
  }

  function iconButton(d: ReviewDecision, extraOnClick?: () => void) {
    const Icon = ICON[d.tone];
    const active = d.key === appliedKey;
    return (
      <button
        type="button"
        title={d.label}
        aria-label={d.label}
        aria-pressed={active}
        disabled={busy === d.key}
        onClick={(e) => {
          e.stopPropagation();
          if (extraOnClick) extraOnClick();
          else void apply(d);
        }}
        className={cn(BTN, active ? ACTIVE[d.tone] : IDLE[d.tone])}
      >
        <Icon className="h-4 w-4" />
      </button>
    );
  }

  if (decisions.length === 0) return null;
  return (
    <div className="flex items-center justify-end gap-1" onClick={(e) => e.stopPropagation()}>
      {decisions.map((d) =>
        d.promptNote ? (
          // ✎ edit: open a note box for HOW it should change, then save note + move.
          <Popover key={d.key} open={noteOpen} onOpenChange={setNoteOpen}>
            <PopoverTrigger asChild>
              {iconButton(d, () => setNote(fieldStr(record.data, cfg.noteAttr)))}
            </PopoverTrigger>
            <PopoverContent align="end" className="w-72" onClick={(e) => e.stopPropagation()}>
              <p className="mb-1.5 text-xs font-medium text-foreground">What should change?</p>
              <Textarea
                value={note}
                onChange={(e) => setNote(e.target.value)}
                rows={3}
                autoFocus
                placeholder="e.g. angle too broad; needs a 2026 source"
                className="text-sm"
              />
              <div className="mt-2 flex justify-end gap-2">
                <Button
                  size="sm"
                  variant="outline"
                  onClick={() => setNoteOpen(false)}
                  disabled={busy === d.key}
                >
                  Cancel
                </Button>
                <Button
                  size="sm"
                  onClick={() => {
                    void apply(d, note).then((ok) => ok && setNoteOpen(false));
                  }}
                  disabled={busy === d.key}
                >
                  {busy === d.key ? 'Saving…' : 'Save'}
                </Button>
              </div>
            </PopoverContent>
          </Popover>
        ) : (
          <span key={d.key}>{iconButton(d)}</span>
        ),
      )}
    </div>
  );
}
