"use client";

import { useState } from "react";
import { toast } from "sonner";
import { Mic, Send, Sparkles, Square, X } from "lucide-react";

import { cn } from "@/lib/utils";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import {
  Dialog,
  DialogContent,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from "@/components/ui/dialog";
import { useBoard } from "@/components/board/board-context";
import { useSpeechRecognition } from "@/hooks/use-speech-recognition";
import { converseAboutTodos, type TodoAiMessage, type TodoAiProposal } from "@/lib/actions/todo-ai";
import type { GroupDTO } from "@/types/board";

const TITLE_MAX = 20;

export function TodoAiDialog({
  group,
  open,
  onOpenChange,
}: {
  group: GroupDTO;
  open: boolean;
  onOpenChange: (open: boolean) => void;
}) {
  const { addTodos } = useBoard();

  const [messages, setMessages] = useState<TodoAiMessage[]>([]);
  const [input, setInput] = useState("");
  const [sending, setSending] = useState(false);
  const [saving, setSaving] = useState(false);
  const [error, setError] = useState<string | null>(null);
  // Set once the model finalizes -- an editable review list the user has
  // to explicitly accept before any of it becomes real to-dos.
  const [pendingTodos, setPendingTodos] = useState<TodoAiProposal[] | null>(null);

  const { supported: micSupported, listening, start, stop } = useSpeechRecognition({
    onFinalResult: (transcript) =>
      setInput((prev) => (prev ? `${prev} ${transcript}` : transcript)),
    onError: setError,
  });

  // Starts fresh every time it's opened -- a half-finished conversation
  // from last time would be confusing to resume out of context.
  function handleOpenChange(next: boolean) {
    if (!next) {
      stop();
      setMessages([]);
      setInput("");
      setError(null);
      setPendingTodos(null);
    }
    onOpenChange(next);
  }

  async function callAi(nextMessages: TodoAiMessage[], forceFinalize: boolean) {
    setError(null);
    setSending(true);
    const result = await converseAboutTodos(
      group.id,
      group.title,
      group.todos.map((t) => t.title),
      nextMessages,
      forceFinalize
    );
    setSending(false);

    if ("error" in result) {
      setError(result.error);
      return;
    }
    if (result.action === "ask") {
      setMessages([...nextMessages, { role: "assistant", content: result.question }]);
    } else {
      setMessages(nextMessages);
      setPendingTodos(result.todos);
    }
  }

  async function handleSend() {
    const text = input.trim();
    if (!text || sending) return;
    if (listening) stop();
    const next: TodoAiMessage[] = [...messages, { role: "user", content: text }];
    setMessages(next);
    setInput("");
    await callAi(next, false);
  }

  async function handleGenerateNow() {
    if (sending || messages.length === 0) return;
    if (listening) stop();
    await callAi(messages, true);
  }

  function updatePendingTodo(index: number, patch: Partial<TodoAiProposal>) {
    setPendingTodos((prev) => prev && prev.map((t, i) => (i === index ? { ...t, ...patch } : t)));
  }

  function removePendingTodo(index: number) {
    setPendingTodos((prev) => prev && prev.filter((_, i) => i !== index));
  }

  function handleDiscard() {
    setPendingTodos(null);
  }

  async function handleAccept() {
    if (!pendingTodos || pendingTodos.length === 0) return;
    setSaving(true);
    const ok = await addTodos(
      group.id,
      group.categoryId,
      pendingTodos.map((t) => ({
        title: t.title.trim(),
        details: t.details?.trim() || undefined,
      }))
    );
    setSaving(false);
    if (ok) {
      toast.success(`Added ${pendingTodos.length} to-do${pendingTodos.length === 1 ? "" : "s"}.`);
      handleOpenChange(false);
    }
  }

  return (
    <Dialog open={open} onOpenChange={handleOpenChange}>
      <DialogContent className="flex max-h-[80vh] flex-col sm:max-w-lg">
        <DialogHeader>
          <DialogTitle className="flex items-center gap-2">
            <Sparkles className="size-4 text-primary" />
            Add using AI — {group.title}
          </DialogTitle>
        </DialogHeader>

        {pendingTodos ? (
          <>
            <div className="flex-1 space-y-3 overflow-y-auto">
              {pendingTodos.map((todo, index) => (
                <div key={index} className="space-y-2 rounded-md border p-3">
                  <div className="flex items-start gap-2">
                    <div className="flex-1 space-y-1">
                      <Label htmlFor={`ai-todo-title-${index}`} className="sr-only">
                        Title
                      </Label>
                      <Input
                        id={`ai-todo-title-${index}`}
                        value={todo.title}
                        maxLength={TITLE_MAX}
                        onChange={(e) => updatePendingTodo(index, { title: e.target.value })}
                      />
                      <p className="text-right text-xs text-muted-foreground">
                        {todo.title.length}/{TITLE_MAX}
                      </p>
                    </div>
                    <Button
                      type="button"
                      variant="ghost"
                      size="icon"
                      className="shrink-0"
                      onClick={() => removePendingTodo(index)}
                      aria-label={`Remove "${todo.title}"`}
                    >
                      <X className="size-4" />
                    </Button>
                  </div>
                  <Textarea
                    value={todo.details ?? ""}
                    onChange={(e) => updatePendingTodo(index, { details: e.target.value })}
                    placeholder="Details (optional)"
                    rows={2}
                    className="resize-none"
                  />
                </div>
              ))}
              {pendingTodos.length === 0 && (
                <p className="rounded-md border border-dashed p-4 text-center text-sm text-muted-foreground">
                  All proposed to-dos were removed. Keep chatting to generate more.
                </p>
              )}
            </div>
            <DialogFooter>
              <Button variant="outline" onClick={handleDiscard} disabled={saving}>
                Keep chatting
              </Button>
              <Button onClick={handleAccept} disabled={saving || pendingTodos.length === 0}>
                {saving
                  ? "Adding…"
                  : `Add ${pendingTodos.length} to-do${pendingTodos.length === 1 ? "" : "s"}`}
              </Button>
            </DialogFooter>
          </>
        ) : (
          <>
            <div className="flex-1 space-y-3 overflow-y-auto">
              {messages.length === 0 ? (
                <p className="rounded-md border border-dashed p-4 text-center text-sm text-muted-foreground">
                  Tell me about the to-dos you need — I&apos;ll ask follow-ups if I need more
                  detail.
                </p>
              ) : (
                messages.map((message, index) => (
                  <div
                    key={index}
                    className={cn(
                      "max-w-[85%] rounded-lg px-3 py-2 text-sm",
                      message.role === "user"
                        ? "ml-auto bg-primary text-primary-foreground"
                        : "bg-muted text-foreground"
                    )}
                  >
                    {message.content}
                  </div>
                ))
              )}
              {sending && (
                <div className="max-w-[85%] rounded-lg bg-muted px-3 py-2 text-sm text-muted-foreground">
                  Thinking…
                </div>
              )}
              {error && <p className="text-sm text-destructive">{error}</p>}
            </div>

            <div className="space-y-2">
              <div className="flex items-end gap-2">
                <Textarea
                  value={input}
                  onChange={(e) => setInput(e.target.value)}
                  onKeyDown={(e) => {
                    if (e.key === "Enter" && !e.shiftKey) {
                      e.preventDefault();
                      handleSend();
                    }
                  }}
                  placeholder={listening ? "Listening…" : "Type or use the microphone…"}
                  rows={2}
                  className="flex-1 resize-none"
                  disabled={sending}
                />
                <div className="flex flex-col gap-2">
                  {micSupported && (
                    <Button
                      type="button"
                      variant={listening ? "destructive" : "outline"}
                      size="icon"
                      onClick={listening ? stop : start}
                      disabled={sending}
                      aria-label={listening ? "Stop recording" : "Record voice input"}
                    >
                      {listening ? <Square className="size-4" /> : <Mic className="size-4" />}
                    </Button>
                  )}
                  <Button
                    type="button"
                    size="icon"
                    onClick={handleSend}
                    disabled={sending || !input.trim()}
                    aria-label="Send"
                  >
                    <Send className="size-4" />
                  </Button>
                </div>
              </div>

              <Button
                type="button"
                variant="ghost"
                size="sm"
                className="text-muted-foreground"
                onClick={handleGenerateNow}
                disabled={sending || messages.length === 0}
              >
                Generate now, using what I&apos;ve said so far
              </Button>
            </div>
          </>
        )}
      </DialogContent>
    </Dialog>
  );
}
