import { useState } from 'react';
import { Copy, RefreshCw, Check, GitFork, Trash2, History, ThumbsUp, ThumbsDown } from 'lucide-react';
import { toast } from 'sonner';
import type { Message } from '@/api/types';
import type { MessageActions } from '@/components/MessageBubble';
import { api } from '@/api/client';
import { sessionEvents } from '@/hooks/sessionEvents';
import { Button } from '@/components/ui/button';
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from '@/components/ui/dialog';

export function ActionRow({
  message,
  actions,
  hiddenSet,
  hasCheckpoint = false,
  restoreDisabled = false,
}: {
  message: Message;
  actions?: MessageActions;
  hiddenSet: Set<string>;
  hasCheckpoint?: boolean;
  restoreDisabled?: boolean;
}) {
  const [justCopied, setJustCopied] = useState(false);
  const [regenerating, setRegenerating] = useState(false);
  const [forking, setForking] = useState(false);
  const [deleteOpen, setDeleteOpen] = useState(false);
  const [deleting, setDeleting] = useState(false);
  const [restoreOpen, setRestoreOpen] = useState(false);
  const [restoring, setRestoring] = useState(false);
  const [feedbackValue, setFeedbackValue] = useState<'up' | 'down' | null>(() => {
    const m = message.metadata;
    return m && m.kind === 'feedback' ? m.value : null;
  });

  async function copy() {
    try {
      await navigator.clipboard.writeText(message.content);
      setJustCopied(true);
      setTimeout(() => setJustCopied(false), 1200);
    } catch (err) {
      toast.error(err instanceof Error ? err.message : 'copy failed');
    }
  }

  async function regenerate() {
    if (regenerating || message.status === 'streaming') return;
    setRegenerating(true);
    try {
      if (actions?.onRegenerate) {
        await actions.onRegenerate(message.chat_id, message.id);
      } else {
        await api.messages.regenerate(message.chat_id, message.id);
      }
    } catch (err) {
      toast.error(err instanceof Error ? err.message : 'regenerate failed');
    } finally {
      setRegenerating(false);
    }
  }

  async function resend() {
    if (!canResend) return;
    try {
      if (actions?.onResend) {
        await actions.onResend(message.chat_id, message.content!);
      } else {
        await api.messages.send(message.chat_id, message.content!);
      }
    } catch (err) {
      toast.error(err instanceof Error ? err.message : 'resend failed');
    }
  }

  async function fork() {
    if (forking || message.status !== 'complete') return;
    setForking(true);
    try {
      if (actions?.onFork) {
        await actions.onFork(message.chat_id, message.id);
      } else {
        const chat = await api.chats.fork(message.chat_id, { messageId: message.id });
        sessionEvents.emit({ type: 'refetch_messages' });
        sessionEvents.emit({ type: 'open_chat_in_new_pane', chat_id: chat.id });
      }
    } catch (err) {
      toast.error(err instanceof Error ? err.message : 'fork failed');
    } finally {
      setForking(false);
    }
  }

  async function confirmDelete() {
    if (deleting) return;
    setDeleting(true);
    try {
      if (actions?.onDelete) {
        await actions.onDelete(message.chat_id, message.id);
      } else {
        await api.messages.remove(message.chat_id, message.id);
      }
      setDeleteOpen(false);
    } catch (err) {
      toast.error(err instanceof Error ? err.message : 'delete failed');
    } finally {
      setDeleting(false);
    }
  }

  async function confirmRestore() {
    if (restoring || !actions?.onRestoreCheckpoint) return;
    setRestoring(true);
    try {
      await actions.onRestoreCheckpoint(message.chat_id, message.id);
      setRestoreOpen(false);
    } catch (err) {
      toast.error(err instanceof Error ? err.message : 'restore failed');
    } finally {
      setRestoring(false);
    }
  }

  async function handleFeedback(value: 'up' | 'down') {
    if (feedbackValue) return; // already voted
    setFeedbackValue(value);
    try {
      await api.messages.feedback(message.chat_id, message.id, value);
      toast.success('Feedback recorded');
    } catch (err) {
      setFeedbackValue(null); // revert optimistic update
      toast.error(err instanceof Error ? err.message : 'Failed to submit feedback');
    }
  }

  const isAssistant = message.role === 'assistant';
  const isUser = message.role === 'user';
  const canRegen = isAssistant && message.status !== 'streaming';
  const canResend = isUser && message.status === 'complete' && !!message.content?.trim();
  const canFork = message.status === 'complete';
  const canDelete = message.status !== 'streaming';
  // write-edit-robustness #4: show "Restore to here" only for a completed
  // assistant message that has a checkpoint AND when the coder wired the
  // callback. Disabled (but visible) during an active turn.
  const canRestore =
    isAssistant &&
    hasCheckpoint &&
    message.status === 'complete' &&
    !!actions?.onRestoreCheckpoint;

  return (
    <>
      <div className="flex gap-1 opacity-0 group-hover:opacity-100 transition-opacity max-md:opacity-100">
        <button
          type="button"
          onClick={() => void copy()}
          className="inline-flex items-center justify-center size-6 rounded text-muted-foreground hover:bg-muted hover:text-foreground max-md:min-h-[44px] max-md:min-w-[44px]"
          aria-label="Copy message"
          title="Copy"
        >
          {justCopied ? <Check className="size-3" /> : <Copy className="size-3" />}
        </button>
        {canResend && (
          <button
            type="button"
            onClick={() => void resend()}
            className="inline-flex items-center justify-center size-6 rounded text-muted-foreground hover:bg-muted hover:text-foreground max-md:min-h-[44px] max-md:min-w-[44px]"
            aria-label="Resend message"
            title="Resend"
          >
            <RefreshCw className="size-3" />
          </button>
        )}
        {isAssistant && (
          <button
            type="button"
            onClick={() => void regenerate()}
            disabled={!canRegen || regenerating}
            className="inline-flex items-center justify-center size-6 rounded text-muted-foreground hover:bg-muted hover:text-foreground disabled:opacity-40 disabled:cursor-not-allowed max-md:min-h-[44px] max-md:min-w-[44px]"
            aria-label="Regenerate message"
            title="Regenerate"
          >
            <RefreshCw className={`size-3 ${regenerating ? 'animate-spin' : ''}`} />
          </button>
        )}
        {isAssistant && (
          <>
            <button
              type="button"
              onClick={() => void handleFeedback('up')}
              disabled={feedbackValue !== null}
              className={`inline-flex items-center justify-center size-6 rounded hover:bg-muted max-md:min-h-[44px] max-md:min-w-[44px] ${
                feedbackValue === 'up'
                  ? 'text-green-500'
                  : 'text-muted-foreground hover:text-foreground disabled:opacity-40 disabled:cursor-not-allowed'
              }`}
              aria-label="Rate as good"
              title="Good response"
            >
              <ThumbsUp className="size-3" />
            </button>
            <button
              type="button"
              onClick={() => void handleFeedback('down')}
              disabled={feedbackValue !== null}
              className={`inline-flex items-center justify-center size-6 rounded hover:bg-muted max-md:min-h-[44px] max-md:min-w-[44px] ${
                feedbackValue === 'down'
                  ? 'text-red-500'
                  : 'text-muted-foreground hover:text-foreground disabled:opacity-40 disabled:cursor-not-allowed'
              }`}
              aria-label="Rate as bad"
              title="Bad response"
            >
              <ThumbsDown className="size-3" />
            </button>
          </>
        )}
        {!hiddenSet.has('fork') && (
          <button
            type="button"
            onClick={() => void fork()}
            disabled={!canFork || forking}
            className="inline-flex items-center justify-center size-6 rounded text-muted-foreground hover:bg-muted hover:text-foreground disabled:opacity-40 disabled:cursor-not-allowed max-md:min-h-[44px] max-md:min-w-[44px]"
            aria-label="Fork from here"
            title="Fork from here"
          >
            <GitFork className="size-3" />
          </button>
        )}
        {!hiddenSet.has('delete') && (
          <button
            type="button"
            onClick={() => setDeleteOpen(true)}
            disabled={!canDelete}
          className="inline-flex items-center justify-center size-6 rounded text-muted-foreground hover:bg-muted hover:text-destructive disabled:opacity-40 disabled:cursor-not-allowed max-md:min-h-[44px] max-md:min-w-[44px]"
          aria-label="Delete message"
          title="Delete message"
        >
          <Trash2 className="size-3" />
        </button>
        )}
        {canRestore && (
          <button
            type="button"
            onClick={() => setRestoreOpen(true)}
            disabled={restoreDisabled || restoring}
            className="inline-flex items-center justify-center size-6 rounded text-muted-foreground hover:bg-muted hover:text-foreground disabled:opacity-40 disabled:cursor-not-allowed max-md:min-h-[44px] max-md:min-w-[44px]"
            aria-label="Restore to here"
            title="Restore worktree to this point"
          >
            <History className="size-3" />
          </button>
        )}
      </div>
      <Dialog
        open={deleteOpen}
        onOpenChange={(open) => {
          if (!deleting) setDeleteOpen(open);
        }}
      >
        <DialogContent>
          <DialogHeader>
            <DialogTitle>Delete this message and all messages after it?</DialogTitle>
            <DialogDescription>
              This removes the selected message and every later message in this chat. This cannot be undone.
            </DialogDescription>
          </DialogHeader>
          <DialogFooter>
            <Button
              variant="outline"
              onClick={() => setDeleteOpen(false)}
              disabled={deleting}
            >
              Cancel
            </Button>
            <Button
              variant="destructive"
              onClick={() => void confirmDelete()}
              disabled={deleting}
            >
              {deleting ? 'Deleting…' : 'Delete'}
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>
      <Dialog
        open={restoreOpen}
        onOpenChange={(open) => {
          if (!restoring) setRestoreOpen(open);
        }}
      >
        <DialogContent>
          <DialogHeader>
            <DialogTitle>Restore to this point?</DialogTitle>
            <DialogDescription>
              This resets the worktree to before this turn, removes every later
              message in this chat, and resets the agent's session. This cannot
              be undone.
            </DialogDescription>
          </DialogHeader>
          <DialogFooter>
            <Button
              variant="outline"
              onClick={() => setRestoreOpen(false)}
              disabled={restoring}
            >
              Cancel
            </Button>
            <Button
              variant="destructive"
              onClick={() => void confirmRestore()}
              disabled={restoring}
            >
              {restoring ? 'Restoring…' : 'Restore'}
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>
    </>
  );
}
