"use client";

import { useState, useEffect } from "react";
import { Separator } from "@/components/ui/separator";
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import {
  Accordion,
  AccordionContent,
  AccordionItem,
  AccordionTrigger,
} from "@/components/ui/accordion";
import {
  Dialog,
  DialogContent,
  DialogHeader,
  DialogTitle,
  DialogDescription,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Png } from "@/lib/png";
import {
  ChevronUp,
  ChevronDown,
  Copy,
  ChevronLeft,
  ChevronRight,
} from "lucide-react";
import {
  CollapsibleContent,
  Collapsible,
  CollapsibleTrigger,
} from "@/components/ui/collapsible";
import { CollapsibleInfobox } from "@/components/ui/collapsible-infobox";
import Script from "next/script";

interface CardDataV2 {
  name: string;
  first_mes: string;
  alternate_greetings?: string[];
  description: string;
  personality: string;
  mes_example: string;
  scenario: string;
  creator?: string;
  creator_notes?: string;
  system_prompt?: string;
  post_history_instructions?: string;
  tags?: string[];
  character_version?: string;
  extensions?: Record<string, unknown>;
}

interface Card {
  id: string;
  data: CardDataV2;
  trackingName?: string;
  spec?: string;
  spec_version?: string;
  avatarUrl?: string;
  hasVersions?: boolean;
  versionCount?: number;
  messageCount?: number;
  alternate_greetings?: string[];
  initialVersion?: CardDataV2;
}

export default function Home() {
  const [isInstructionsOpen, setIsInstructionsOpen] = useState(false);
  const [cards, setCards] = useState<Card[]>([]);
  const [dialogOpen, setDialogOpen] = useState(false);
  const [selectedCardIndex, setSelectedCardIndex] = useState<number | null>(
    null
  );
  const [characterUrl, setCharacterUrl] = useState("");
  const [avatarPath, setAvatarPath] = useState("");
  const [isMetadataOpen, setIsMetadataOpen] = useState(false);
  const [isRefreshing, setIsRefreshing] = useState(false);
  const [changesDialogOpen, setChangesDialogOpen] = useState(false);
  const [selectedChanges, setSelectedChanges] = useState<any>(null);
  const [showFullText, setShowFullText] = useState(false);
  const [altGreetingIndexById, setAltGreetingIndexById] = useState<
    Record<string, number>
  >({});

  const [proxyUrl, setProxyUrl] = useState(
    "https://sucker.severian.dev/api/proxy"
  );

  const fetchCards = async () => {
    try {
      setIsRefreshing(true);
      const response = await fetch("/api/proxy");
      const data = await response.json();
      if (data.cards) {
        setCards((prevCards) => {
          return data.cards.map((newCard: Card) => ({
            ...newCard,
            avatarUrl:
              prevCards.find((c) => c.id === newCard.id)?.avatarUrl ||
              newCard.avatarUrl,
          }));
        });
      }
    } catch (error) {
      console.error("Error fetching cards:", error);
    } finally {
      setIsRefreshing(false);
    }
  };

  useEffect(() => {
    fetchCards();
  }, []);

  useEffect(() => {
    if (typeof window !== "undefined") {
      const origin = window.location.origin;
      setProxyUrl(`${origin}/api/proxy`);
    }
  }, []);

  const downloadJson = (card: Card) => {
    // Use initial version for download, or current version if no initial version available
    const chosen = card.initialVersion || card.data;
    const downloadData = {
      data: {
        name: chosen.name,
        first_mes: chosen.first_mes,
        alternate_greetings: chosen.alternate_greetings || [],
        description: chosen.description,
        personality: chosen.personality,
        mes_example: chosen.mes_example,
        scenario: chosen.scenario,
        creator: (chosen as any).creator || "",
        creator_notes: (chosen as any).creator_notes || "",
        system_prompt: (chosen as any).system_prompt || "",
        post_history_instructions:
          (chosen as any).post_history_instructions || "",
        tags: (chosen as any).tags || [],
        character_version: (chosen as any).character_version || "1",
        extensions: (chosen as any).extensions || {},
      },
      spec: card.spec || "chara_card_v2",
      spec_version: card.spec_version || "2.0",
    };

    const element = document.createElement("a");
    const file = new Blob([JSON.stringify(downloadData, null, 2)], {
      type: "application/json",
    });
    element.href = URL.createObjectURL(file);
    element.download = `${(card.initialVersion?.name || card.data.name).replace(
      /[^a-zA-Z0-9\-_]/g,
      "_"
    )}.json`;
    document.body.appendChild(element);
    element.click();
    document.body.removeChild(element);
  };

  const downloadChanges = async (card: Card) => {
    try {
      const response = await fetch(`/api/proxy?changes=true&cardId=${card.id}`);
      if (!response.ok) {
        throw new Error("Failed to fetch changes");
      }

      const changesData = await response.json();
      const element = document.createElement("a");
      const file = new Blob([JSON.stringify(changesData, null, 2)], {
        type: "application/json",
      });
      element.href = URL.createObjectURL(file);
      element.download = `${(
        card.initialVersion?.name || card.data.name
      ).replace(/[^a-zA-Z0-9\-_]/g, "_")}_changes.json`;
      document.body.appendChild(element);
      element.click();
      document.body.removeChild(element);
    } catch (error) {
      console.error("Error downloading changes:", error);
      alert(
        "Failed to download changes. The card may not have version history."
      );
    }
  };

  const viewChanges = async (card: Card) => {
    try {
      const response = await fetch(`/api/proxy?changes=true&cardId=${card.id}`);
      if (!response.ok) {
        throw new Error("Failed to fetch changes");
      }

      const changesData = await response.json();
      setSelectedChanges(changesData);
      setShowFullText(false); // Reset to diff view by default
      setChangesDialogOpen(true);
    } catch (error) {
      console.error("Error fetching changes:", error);
      alert("Failed to fetch changes. The card may not have version history.");
    }
  };

  const downloadPng = async (card: Card, cardId: string) => {
    if (!card.avatarUrl) return;

    try {
      const img = new Image();
      img.src = `/api/proxy/image?url=${encodeURIComponent(card.avatarUrl)}`;

      await new Promise((resolve, reject) => {
        img.onload = resolve;
        img.onerror = reject;
      });

      const canvas = document.createElement("canvas");
      canvas.width = img.width;
      canvas.height = img.height;
      const ctx = canvas.getContext("2d");
      if (!ctx) throw new Error("Could not get canvas context");

      ctx.drawImage(img, 0, 0);
      const pngBlob = await new Promise<Blob>((resolve) => {
        canvas.toBlob((blob) => {
          if (blob) resolve(blob);
          else throw new Error("Could not convert to PNG");
        }, "image/png");
      });

      const arrayBuffer = await pngBlob.arrayBuffer();

      // Use initial version for PNG embedding, or current version if no initial version available
      const chosen = card.initialVersion || card.data;
      const pngData = {
        data: {
          name: chosen.name,
          first_mes: chosen.first_mes,
          alternate_greetings: chosen.alternate_greetings || [],
          description: chosen.description,
          personality: chosen.personality,
          mes_example: chosen.mes_example,
          scenario: chosen.scenario,
          creator: (chosen as any).creator || "",
          creator_notes: (chosen as any).creator_notes || "",
          system_prompt: (chosen as any).system_prompt || "",
          post_history_instructions:
            (chosen as any).post_history_instructions || "",
          tags: (chosen as any).tags || [],
          character_version: (chosen as any).character_version || "1",
          extensions: (chosen as any).extensions || {},
        },
        spec: card.spec || "chara_card_v2",
        spec_version: card.spec_version || "2.0",
      };

      const cardData = JSON.stringify(pngData);

      const newImageData = Png.Generate(arrayBuffer, cardData);
      const newFileName = `${
        (card.initialVersion?.name || card.data.name).replace(
          /[^a-zA-Z0-9\-_]/g,
          "_"
        ) || "character"
      }.png`;
      const newFile = new File([new Uint8Array(newImageData)], newFileName, {
        type: "image/png",
      });

      const link = URL.createObjectURL(newFile);
      const a = document.createElement("a");
      a.download = newFileName;
      a.href = link;
      a.click();

      URL.revokeObjectURL(link);
    } catch (error) {
      console.error("Error generating PNG:", error);
      alert("Couldn't export this character card, sorry.");
    }
  };

  const handleOpenDialog = (index: number) => {
    setSelectedCardIndex(index);
    setDialogOpen(true);
    setCharacterUrl("");
    setAvatarPath("");
    setIsMetadataOpen(false);
  };

  const handleOpenMetadata = () => {
    const match = characterUrl.match(/characters\/([\w-]+)/);
    if (match && match[1]) {
      const characterId = match[1].split("_")[0];
      window.open(
        `https://janitorai.com/hampter/characters/${characterId}`,
        "_blank"
      );
      setIsMetadataOpen(true);
    }
  };

  const handleFetchAvatar = async () => {
    if (selectedCardIndex === null) return;

    try {
      const avatarUrl = `https://ella.janitorai.com/bot-avatars/${avatarPath}`;
      const updatedCards = [...cards];
      updatedCards[selectedCardIndex] = {
        ...updatedCards[selectedCardIndex],
        avatarUrl,
      };
      setCards(updatedCards);
      setDialogOpen(false);
    } catch (error) {
      console.error("Error fetching avatar:", error);
    }
  };

  const copyToClipboard = (text: string) => {
    navigator.clipboard.writeText(text);
  };

  return (
    <main className="min-h-screen bg-background text-foreground">
      <Script
        src="https://www.googletagmanager.com/gtag/js?id=G-YVD6QFSR71"
        strategy="afterInteractive"
      />
      <Script id="gtag-init" strategy="afterInteractive">
        {`window.dataLayer = window.dataLayer || [];
        function gtag(){dataLayer.push(arguments);} 
        gtag('js', new Date());
        gtag('config', 'G-YVD6QFSR71');`}
      </Script>
      <div className="container mx-auto px-4 py-8">
        <div className="flex justify-between items-center mb-4">
          <div>
            <h1 className="text-3xl font-bold">Sucker v2.1</h1>
            <p className="text-sm text-muted-foreground">
              Just some notes this time
            </p>
          </div>
          <Button
            onClick={fetchCards}
            variant="outline"
            disabled={isRefreshing}
          >
            {isRefreshing ? "Refreshing..." : "Refresh"}
          </Button>
        </div>

        <Separator className="my-4" />
        {/* Collapsible infoboxes */}
        {[
          {
            title: "(Dec 2025) A note about fetching avatars",
            content: (
              <>
                <p className="text-sm text-muted-foreground">
                  The platform you suck from has implemented limited visibility
                  of metadata for certain content with a particular 'obscenity
                  rating'. This means that in some cases, the Fetch Avatar flow
                  here will show a 404 - character not found error at the end.
                </p>
                <p className="text-sm text-muted-foreground">
                  Sometimes (but not always), the avatar URL can still be
                  fetched after a day or two since the bot was published.
                </p>
                <p className="text-sm text-muted-foreground">
                  As of this moment, can't really find a fix for it, so you'll
                  have to download the image yourself and just add the image to
                  the card someplace else.
                </p>
              </>
            ),
          },
          {
            title:
              "(Oct 2025) V2 charcard format, multi-turn support for scripts/lorebooks, alternate greetings.",
            content: (
              <p className="text-sm text-muted-foreground">
                Sucker now tracks changes to character descriptions and
                scenarios across multiple messages. Cards with multiple versions
                show a version badge and offer a "Download Changes" button to
                get the change history.
                <br />
                Alternate greetings are also supported. Sucker will provide you
                with a conversation ID that you can use to start off a new chat
                when capturing alternate greetings, send it as first message
                instead of the character name.
                <br />
                Directions are updated below. Make sure you read 'em.
              </p>
            ),
          },
          {
            title: "List of mirrors",
            content: (
              <>
                <p className="text-sm text-muted-foreground">
                  Sucker goes down sometimes on severian.dev because I use the
                  server for other stuff. Here's a full list of existing sucker
                  instances (thanks to those who signed up for it!):
                </p>
                <ul className="text-sm flex flex-col sm:flex-row list-none">
                  <li className="after:content-none sm:after:mx-2 sm:after:content-['•'] sm:last:after:content-none">
                    <a
                      className="text-yellow-600"
                      href="https://sucker.severian.dev"
                    >
                      severian.dev
                    </a>
                  </li>
                  <li className="after:content-none sm:after:mx-2 sm:after:content-['•'] sm:last:after:content-none">
                    <a
                      className="text-yellow-600"
                      href="https://sucker.trashpanda.land"
                    >
                      trashpanda.land
                    </a>
                  </li>
                  <li className="after:content-none sm:after:mx-2 sm:after:content-['•'] sm:last:after:content-none">
                    <a
                      className="text-yellow-600"
                      href="https://sucker.hitani.me"
                    >
                      hitani.me
                    </a>
                  </li>
                  <li className="after:content-none sm:after:mx-2 sm:after:content-['•'] sm:last:after:content-none">
                    <a
                      className="text-yellow-600"
                      href="https://succ.portalnexus.link"
                    >
                      portalnexus.link
                    </a>
                  </li>
                  <li className="after:content-none sm:after:mx-2 sm:after:content-['•'] sm:last:after:content-none">
                    <a
                      className="text-yellow-600"
                      href="https://sucker.lemuria.dev"
                    >
                      lemuria.dev
                    </a>
                  </li>
                </ul>
                <p className="text-sm text-muted-foreground">
                  <br />
                  If you're interested in hosting your own sucker instance, lmk
                  via Discord: @lyseverian, I've made the GH repo private for
                  now. Or send me a message if there's anything you think that
                  could be added here, open to suggestions.
                </p>
              </>
            ),
          },
        ].map((infobox, idx) => (
          <CollapsibleInfobox
            key={infobox.title}
            title={infobox.title}
            defaultOpen={false}
          >
            {infobox.content}
          </CollapsibleInfobox>
        ))}

        <Collapsible
          open={isInstructionsOpen}
          onOpenChange={setIsInstructionsOpen}
          className="w-full mb-8"
        >
          <div className="flex items-center justify-between">
            <h2 className="text-2xl font-semibold">How to Use</h2>
            <CollapsibleTrigger asChild>
              <Button variant="ghost" size="sm" className="w-9 p-0">
                {isInstructionsOpen ? (
                  <ChevronUp className="h-4 w-4" />
                ) : (
                  <ChevronDown className="h-4 w-4" />
                )}
                <span className="sr-only">Toggle instructions</span>
              </Button>
            </CollapsibleTrigger>
          </div>
          <CollapsibleContent className="mt-4">
            <div className="prose dark:prose-invert max-w-none">
              <p className="mb-2">
                Follow every instruction here to the letter because it's all you
                need to know and I have no intent of helping you further.
              </p>
              <ol className="list-decimal list-inside">
                <li className="mb-2">
                  Put <code style={{ color: "#fff0b9" }}>{proxyUrl}</code> in
                  your API settings, any value for model and key.
                </li>
                <li className="mb-2">
                  REQUIRED: Set your custom prompt to{" "}
                  <code style={{ color: "#fff0b9" }}>&lt;.&gt;</code>
                </li>
                <li className="mb-2">
                  REQUIRED: Set your persona (or create a new one) with the name{" "}
                  <code style={{ color: "#fff0b9" }}>&#123;user&#125;</code> and
                  the description should only have{" "}
                  <code style={{ color: "#fff0b9" }}>.</code> in it.
                </li>
                <li className="mb-2">
                  Save settings and refresh the page. Not this page. <i>That</i>{" "}
                  page.
                </li>
                <li className="mb-2">Start a new chat with a character.</li>
                <li className="mb-2">
                  Char name inference is implemented: if you send just a dot:{" "}
                  <code style={{ color: "#fff0b9" }}>.</code>, sucker will use
                  the inferred name from the persona tag, or you can send the
                  character name yourself.
                </li>
                <li className="mb-2">
                  Hit the Refresh button here, and the cards should appear here.
                </li>
                <li className="mb-2">
                  If you're interested in capturing alternate greetings, start a
                  new chat and send the conversation ID as first message instead
                  of the character name. The format is{" "}
                  <code style={{ color: "#fff0b9" }}>
                    [sucker:conv=conversationId]
                  </code>{" "}
                  which you'll be given when creating a new card.
                </li>
                <li className="mb-2">
                  You can also send more messages with possible keywords to
                  trigger scripts/lorebooks. Sucker will track changes to the
                  description and scenario fields. Cards with multiple versions
                  will show a version badge and offer a "Download Changes"
                  button to get a detailed change history with timestamps.
                  Unfortunately, lorebook creation is out of scope at the
                  moment, but you can use the changes detected to modify the
                  character card yourself post-export.
                </li>
                <li className="mb-2">
                  Download the JSON files or go through a little more effort to
                  get PNGs instead.
                </li>
              </ol>
              <p className="mb-2">
                Extractions will only last for 10 minutes, after which they're
                discarded. Reloading the page will remove any attached avatars.
                I'm not storing shit.
              </p>
            </div>
          </CollapsibleContent>
        </Collapsible>

        <div className="max-w-3xl mx-auto space-y-6">
          {cards.length === 0 ? (
            <Card>
              <CardContent className="pt-6 text-center text-muted-foreground">
                <p>No extractions yet.</p>
              </CardContent>
            </Card>
          ) : (
            cards.map((card, index) => (
              <Card key={index}>
                <CardContent className="pt-6">
                  <Accordion type="single" collapsible className="w-full">
                    <AccordionItem value={`card-${index}`}>
                      <AccordionTrigger className="text-xl font-semibold">
                        <div className="flex items-center gap-2">
                          {card.initialVersion?.name ||
                            card.data?.name ||
                            "Unnamed Card"}
                          <div className="flex gap-1">
                            {card.hasVersions && (
                              <span className="text-sm bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200 px-2 py-1 rounded-full">
                                v{card.versionCount}
                              </span>
                            )}
                            {card.messageCount && card.messageCount > 1 && (
                              <span className="text-sm bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200 px-2 py-1 rounded-full">
                                {card.messageCount} msgs
                              </span>
                            )}
                          </div>
                        </div>
                      </AccordionTrigger>
                      <AccordionContent>
                        <div id={`card-${index}`} className="space-y-4 mt-4">
                          {(card.initialVersion?.description ||
                            card.data?.description) && (
                            <Accordion type="single" collapsible>
                              <AccordionItem value="description">
                                <AccordionTrigger>Description</AccordionTrigger>
                                <AccordionContent>
                                  <div className="flex justify-between">
                                    <pre className="whitespace-pre-wrap font-sans text-sm">
                                      {card.initialVersion?.description ||
                                        card.data.description}
                                    </pre>
                                    <Button
                                      variant="ghost"
                                      size="icon"
                                      onClick={(e) => {
                                        e.stopPropagation();
                                        copyToClipboard(
                                          card.initialVersion?.description ||
                                            card.data.description
                                        );
                                      }}
                                    >
                                      <Copy className="h-4 w-4" />
                                    </Button>
                                  </div>
                                </AccordionContent>
                              </AccordionItem>
                            </Accordion>
                          )}
                          {(card.initialVersion?.first_mes ||
                            card.data?.first_mes) && (
                            <Accordion type="single" collapsible>
                              <AccordionItem value="first-message">
                                <AccordionTrigger>
                                  First Message
                                </AccordionTrigger>
                                <AccordionContent>
                                  <div className="flex justify-between">
                                    <pre className="whitespace-pre-wrap font-sans text-sm">
                                      {card.initialVersion?.first_mes ||
                                        card.data.first_mes}
                                    </pre>
                                    <Button
                                      variant="ghost"
                                      size="icon"
                                      onClick={(e) => {
                                        e.stopPropagation();
                                        copyToClipboard(
                                          card.initialVersion?.first_mes ||
                                            card.data.first_mes
                                        );
                                      }}
                                    >
                                      <Copy className="h-4 w-4" />
                                    </Button>
                                  </div>
                                </AccordionContent>
                              </AccordionItem>
                            </Accordion>
                          )}
                          {card.alternate_greetings &&
                            card.alternate_greetings.length > 0 && (
                              <div className="mt-4">
                                <div className="flex items-center justify-between mb-2">
                                  <h4 className="font-medium">{`Alternate Greetings (${
                                    card.alternate_greetings?.length || 0
                                  })`}</h4>
                                  <div className="flex items-center gap-2">
                                    <Button
                                      variant="ghost"
                                      size="icon"
                                      onClick={() => {
                                        const greetings =
                                          card.alternate_greetings || [];
                                        if (greetings.length === 0) return;
                                        setAltGreetingIndexById((prev) => {
                                          const current = prev[card.id] ?? 0;
                                          const next =
                                            (current - 1 + greetings.length) %
                                            greetings.length;
                                          return { ...prev, [card.id]: next };
                                        });
                                      }}
                                    >
                                      <ChevronLeft className="h-4 w-4" />
                                    </Button>
                                    <Button
                                      variant="ghost"
                                      size="icon"
                                      onClick={() => {
                                        const greetings =
                                          card.alternate_greetings || [];
                                        if (greetings.length === 0) return;
                                        setAltGreetingIndexById((prev) => {
                                          const current = prev[card.id] ?? 0;
                                          const next =
                                            (current + 1) % greetings.length;
                                          return { ...prev, [card.id]: next };
                                        });
                                      }}
                                    >
                                      <ChevronRight className="h-4 w-4" />
                                    </Button>
                                  </div>
                                </div>
                                {(() => {
                                  const greetings =
                                    card.alternate_greetings || [];
                                  const index =
                                    altGreetingIndexById[card.id] ?? 0;
                                  const current = greetings.length
                                    ? greetings[index % greetings.length]
                                    : "";
                                  return (
                                    <div className="flex justify-between">
                                      <pre className="whitespace-pre-wrap font-sans text-sm">
                                        {current}
                                      </pre>
                                      <Button
                                        variant="ghost"
                                        size="icon"
                                        onClick={() => {
                                          if (!current) return;
                                          copyToClipboard(current);
                                        }}
                                      >
                                        <Copy className="h-4 w-4" />
                                      </Button>
                                    </div>
                                  );
                                })()}
                              </div>
                            )}
                          {(card.initialVersion?.scenario ||
                            card.data?.scenario) && (
                            <Accordion type="single" collapsible>
                              <AccordionItem value="scenario">
                                <AccordionTrigger>Scenario</AccordionTrigger>
                                <AccordionContent>
                                  <div className="flex justify-between">
                                    <pre className="whitespace-pre-wrap font-sans text-sm">
                                      {card.initialVersion?.scenario ||
                                        card.data.scenario}
                                    </pre>
                                    <Button
                                      variant="ghost"
                                      size="icon"
                                      onClick={(e) => {
                                        e.stopPropagation();
                                        copyToClipboard(
                                          card.initialVersion?.scenario ||
                                            card.data.scenario
                                        );
                                      }}
                                    >
                                      <Copy className="h-4 w-4" />
                                    </Button>
                                  </div>
                                </AccordionContent>
                              </AccordionItem>
                            </Accordion>
                          )}
                          {(card.initialVersion?.mes_example ||
                            card.data?.mes_example) && (
                            <Accordion type="single" collapsible>
                              <AccordionItem value="example-messages">
                                <AccordionTrigger>
                                  Example Messages
                                </AccordionTrigger>
                                <AccordionContent>
                                  <div className="flex justify-between">
                                    <pre className="whitespace-pre-wrap font-sans text-sm">
                                      {card.initialVersion?.mes_example ||
                                        card.data.mes_example}
                                    </pre>
                                    <Button
                                      variant="ghost"
                                      size="icon"
                                      onClick={(e) => {
                                        e.stopPropagation();
                                        copyToClipboard(
                                          card.initialVersion?.mes_example ||
                                            card.data.mes_example
                                        );
                                      }}
                                    >
                                      <Copy className="h-4 w-4" />
                                    </Button>
                                  </div>
                                </AccordionContent>
                              </AccordionItem>
                            </Accordion>
                          )}
                          {(card.initialVersion?.personality ||
                            card.data?.personality) && (
                            <Accordion type="single" collapsible>
                              <AccordionItem value="personality">
                                <AccordionTrigger>Personality</AccordionTrigger>
                                <AccordionContent>
                                  <div className="flex justify-between">
                                    <pre className="whitespace-pre-wrap font-sans text-sm">
                                      {card.initialVersion?.personality ||
                                        card.data.personality}
                                    </pre>
                                    <Button
                                      variant="ghost"
                                      size="icon"
                                      onClick={(e) => {
                                        e.stopPropagation();
                                        copyToClipboard(
                                          card.initialVersion?.personality ||
                                            card.data.personality
                                        );
                                      }}
                                    >
                                      <Copy className="h-4 w-4" />
                                    </Button>
                                  </div>
                                </AccordionContent>
                              </AccordionItem>
                            </Accordion>
                          )}
                        </div>
                      </AccordionContent>
                    </AccordionItem>
                  </Accordion>
                  <div className="flex flex-row space-x-2 mt-6">
                    <Button
                      onClick={() => downloadJson(card)}
                      variant="default"
                    >
                      Download JSON
                    </Button>
                    {card.hasVersions && (
                      <>
                        <Button
                          onClick={() => viewChanges(card)}
                          variant="outline"
                        >
                          View Changes
                        </Button>
                        <Button
                          onClick={() => downloadChanges(card)}
                          variant="outline"
                        >
                          Download Changes
                        </Button>
                      </>
                    )}
                    {!card.avatarUrl ? (
                      <Button
                        onClick={() => handleOpenDialog(index)}
                        variant="outline"
                      >
                        Fetch Avatar (required for PNG)
                      </Button>
                    ) : (
                      <Button
                        onClick={() => downloadPng(card, `card-${index}`)}
                        variant="default"
                        disabled={!card.avatarUrl}
                      >
                        Download PNG
                      </Button>
                    )}
                  </div>
                </CardContent>
              </Card>
            ))
          )}
        </div>
      </div>

      <Dialog open={dialogOpen} onOpenChange={setDialogOpen}>
        <DialogContent>
          <DialogHeader>
            <DialogTitle>
              {isMetadataOpen ? "Enter Avatar Path" : "Enter Character URL"}
            </DialogTitle>
            <DialogDescription>
              {isMetadataOpen
                ? "Look for the avatar field in the opened tab and paste the value here."
                : "Enter the Janitor character URL (https://janitorai.com/characters/...)."}
            </DialogDescription>
          </DialogHeader>

          {isMetadataOpen ? (
            <div className="space-y-4">
              <Input
                placeholder="id.webp"
                value={avatarPath}
                onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
                  setAvatarPath(e.target.value)
                }
              />
              <Button onClick={handleFetchAvatar} className="w-full">
                Fetch Avatar
              </Button>
            </div>
          ) : (
            <div className="space-y-4">
              <Input
                placeholder="https://janitorai.com/characters/..."
                value={characterUrl}
                onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
                  setCharacterUrl(e.target.value)
                }
              />
              <p className="text-sm text-muted-foreground">
                Upon clicking this button, a new tab will open with the
                character's metadata. Look for the avatar field and copy the
                value before returning to this page.
              </p>
              <Button onClick={handleOpenMetadata} className="w-full">
                Open Metadata
              </Button>
            </div>
          )}
        </DialogContent>
      </Dialog>

      <Dialog open={changesDialogOpen} onOpenChange={setChangesDialogOpen}>
        <DialogContent className="max-w-4xl max-h-[80vh] overflow-y-auto">
          <DialogHeader>
            <DialogTitle>
              Change History: {selectedChanges?.cardName}
            </DialogTitle>
            <DialogDescription className="flex items-center justify-between">
              <span>
                Version history showing changes to description and scenario
                fields
              </span>
              <Button
                variant="outline"
                size="sm"
                onClick={() => setShowFullText(!showFullText)}
              >
                {showFullText ? "Show Changes Only" : "Show Full Text"}
              </Button>
            </DialogDescription>
          </DialogHeader>

          {selectedChanges && (
            <div className="space-y-4">
              <div className="grid grid-cols-2 gap-4 text-sm">
                <div>
                  <strong>Total Versions:</strong>{" "}
                  {selectedChanges.totalVersions}
                </div>
                <div>
                  <strong>Current Version:</strong>{" "}
                  {selectedChanges.currentVersion}
                </div>
                <div>
                  <strong>Description Changes:</strong>{" "}
                  {selectedChanges.summary.descriptionChanges}
                </div>
                <div>
                  <strong>Scenario Changes:</strong>{" "}
                  {selectedChanges.summary.scenarioChanges}
                </div>
              </div>

              <Separator />

              <div className="space-y-4">
                <h3 className="text-lg font-semibold">Version History</h3>
                {selectedChanges.versions.map((version: any, index: number) => (
                  <div key={version.version} className="border rounded-lg p-4">
                    <div className="flex justify-between items-center mb-2">
                      <h4 className="font-semibold">
                        Version {version.version} ({version.changeType})
                      </h4>
                      <div className="text-sm text-muted-foreground">
                        {new Date(version.timestamp).toLocaleString()}
                        {version.messageCount &&
                          ` • Message ${version.messageCount}`}
                      </div>
                    </div>

                    {version.changes.description && (
                      <div className="mb-3">
                        <h5 className="font-medium text-sm mb-1">
                          Description Change:
                        </h5>
                        {version.changeType === "initial" ? (
                          <div className="bg-blue-50 dark:bg-blue-950 p-2 rounded text-sm">
                            <strong>Initial Content:</strong>{" "}
                            {version.changes.description.new}
                          </div>
                        ) : (
                          <div className="space-y-2">
                            {version.addedText?.description && (
                              <div className="bg-green-50 dark:bg-green-950 p-2 rounded text-sm">
                                <div className="flex justify-between items-start">
                                  <div>
                                    <strong>Added:</strong>{" "}
                                    {version.addedText.description}
                                  </div>
                                  <Button
                                    variant="ghost"
                                    size="icon"
                                    className="ml-2 h-6 w-6"
                                    onClick={(e) => {
                                      e.stopPropagation();
                                      copyToClipboard(
                                        version.addedText.description
                                      );
                                    }}
                                  >
                                    <Copy className="h-3 w-3" />
                                  </Button>
                                </div>
                              </div>
                            )}
                            {version.removedText?.description && (
                              <div className="bg-red-50 dark:bg-red-950 p-2 rounded text-sm">
                                <strong>Removed:</strong>{" "}
                                {version.removedText.description}
                              </div>
                            )}
                            {showFullText && (
                              <div className="space-y-1 mt-2 pt-2 border-t">
                                <div className="bg-gray-50 dark:bg-gray-950 p-2 rounded text-xs">
                                  <strong>Full Old:</strong>{" "}
                                  {version.changes.description.old}
                                </div>
                                <div className="bg-gray-50 dark:bg-gray-950 p-2 rounded text-xs">
                                  <strong>Full New:</strong>{" "}
                                  {version.changes.description.new}
                                </div>
                              </div>
                            )}
                          </div>
                        )}
                      </div>
                    )}

                    {version.changes.scenario && (
                      <div>
                        <h5 className="font-medium text-sm mb-1">
                          Scenario Change:
                        </h5>
                        {version.changeType === "initial" ? (
                          <div className="bg-blue-50 dark:bg-blue-950 p-2 rounded text-sm">
                            <strong>Initial Content:</strong>{" "}
                            {version.changes.scenario.new}
                          </div>
                        ) : (
                          <div className="space-y-2">
                            {version.addedText?.scenario && (
                              <div className="bg-green-50 dark:bg-green-950 p-2 rounded text-sm">
                                <div className="flex justify-between items-start">
                                  <div>
                                    <strong>Added:</strong>{" "}
                                    {version.addedText.scenario}
                                  </div>
                                  <Button
                                    variant="ghost"
                                    size="icon"
                                    className="ml-2 h-6 w-6"
                                    onClick={(e) => {
                                      e.stopPropagation();
                                      copyToClipboard(
                                        version.addedText.scenario
                                      );
                                    }}
                                  >
                                    <Copy className="h-3 w-3" />
                                  </Button>
                                </div>
                              </div>
                            )}
                            {version.removedText?.scenario && (
                              <div className="bg-red-50 dark:bg-red-950 p-2 rounded text-sm">
                                <strong>Removed:</strong>{" "}
                                {version.removedText.scenario}
                              </div>
                            )}
                            {showFullText && (
                              <div className="space-y-1 mt-2 pt-2 border-t">
                                <div className="bg-gray-50 dark:bg-gray-950 p-2 rounded text-xs">
                                  <strong>Full Old:</strong>{" "}
                                  {version.changes.scenario.old}
                                </div>
                                <div className="bg-gray-50 dark:bg-gray-950 p-2 rounded text-xs">
                                  <strong>Full New:</strong>{" "}
                                  {version.changes.scenario.new}
                                </div>
                              </div>
                            )}
                          </div>
                        )}
                      </div>
                    )}
                  </div>
                ))}
              </div>
            </div>
          )}
        </DialogContent>
      </Dialog>
    </main>
  );
}
