"use client";

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

import { cn } from "@/lib/utils";
import { Button } from "@/components/ui/button";
import { Textarea } from "@/components/ui/textarea";
import {
  Dialog,
  DialogContent,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from "@/components/ui/dialog";
import { MarkdownPreview } from "@/components/markdown/markdown-widgets";
import { useBoard } from "@/components/board/board-context";
import { useSpeechRecognition } from "@/hooks/use-speech-recognition";
import { converseAboutGroup, type GroupAiMessage } from "@/lib/actions/group-ai";
import type { GroupDTO } from "@/types/board";

export function GroupAiDialog({
  group,
  open,
  onOpenChange,
}: {
  group: GroupDTO;
  open: boolean;
  onOpenChange: (open: boolean) => void;
}) {
  const { saveNote } = useBoard();
  const { resolvedTheme } = useTheme();
  const colorMode = resolvedTheme === "dark" ? "dark" : "light";

  const [messages, setMessages] = useState<GroupAiMessage[]>([]);
  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 -- shown as a review step the user has
  // to explicitly accept before it touches the group's real notes.
  const [pendingNotes, setPendingNotes] = useState<string | 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);
      setPendingNotes(null);
    }
    onOpenChange(next);
  }

  async function callAi(nextMessages: GroupAiMessage[], forceFinalize: boolean) {
    setError(null);
    setSending(true);
    const result = await converseAboutGroup(group.id, group.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);
      setPendingNotes(result.notes);
    }
  }

  async function handleSend() {
    const text = input.trim();
    if (!text || sending) return;
    if (listening) stop();
    const next: GroupAiMessage[] = [...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);
  }

  async function handleAccept() {
    if (!pendingNotes) return;
    setSaving(true);
    const ok = await saveNote(group.id, group.categoryId, pendingNotes);
    setSaving(false);
    if (ok) {
      toast.success("Notes updated.");
      handleOpenChange(false);
    }
  }

  function handleDiscard() {
    setPendingNotes(null);
  }

  const hasExistingNotes = group.noteContent.trim().length > 0;

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

        {pendingNotes ? (
          <>
            <div className="flex-1 space-y-2 overflow-y-auto">
              {hasExistingNotes && (
                <p className="rounded-md border border-dashed p-2 text-xs text-muted-foreground">
                  This will replace the group&apos;s existing notes.
                </p>
              )}
              <div className="max-h-[50vh] overflow-y-auto rounded-md border p-4">
                <MarkdownPreview source={pendingNotes} />
              </div>
            </div>
            <DialogFooter>
              <Button variant="outline" onClick={handleDiscard} disabled={saving}>
                Keep chatting
              </Button>
              <Button onClick={handleAccept} disabled={saving}>
                {saving ? "Saving…" : hasExistingNotes ? "Replace notes" : "Save notes"}
              </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 this group — what it&apos;s for, or what should be tracked here.
                </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>
  );
}
