import { CheckIcon, XMarkIcon } from "@heroicons/react/20/solid";
import { ClipboardIcon } from "@heroicons/react/24/outline";
import { AnimatePresence, motion } from "framer-motion";
import { Suspense, useCallback, useEffect, useRef, useState } from "react";
import { SparkleListIcon } from "~/assets/icons/SparkleListIcon";
import { Button } from "~/components/primitives/Buttons";
import { StreamdownRenderer } from "~/components/code/StreamdownRenderer";
import { Header3 } from "~/components/primitives/Headers";
import { Paragraph } from "~/components/primitives/Paragraph";
import { Spinner } from "~/components/primitives/Spinner";
import { SimpleTooltip } from "~/components/primitives/Tooltip";
import { useEnvironment } from "~/hooks/useEnvironment";
import { useOrganization } from "~/hooks/useOrganizations";
import { useProject } from "~/hooks/useProject";
import { cn } from "~/utils/cn";

type StreamEventType =
  | { type: "thinking"; content: string }
  | { type: "result"; success: true; payload: string }
  | { type: "result"; success: false; error: string };

export function AIPayloadTabContent({
  onPayloadGenerated,
  payloadSchema,
  taskIdentifier,
  getCurrentPayload,
  generateButtonLabel = "Generate payload",
  placeholder,
  examplePromptsOverride,
  isAgent = false,
  showExamplePromptsHeader = true,
}: {
  onPayloadGenerated: (payload: string) => void;
  payloadSchema?: unknown;
  taskIdentifier: string;
  getCurrentPayload?: () => string;
  generateButtonLabel?: string;
  placeholder?: string;
  examplePromptsOverride?: string[];
  isAgent?: boolean;
  showExamplePromptsHeader?: boolean;
}) {
  const [prompt, setPrompt] = useState("");
  const [isLoading, setIsLoading] = useState(false);
  const isLoadingRef = useRef(false);
  const [thinking, setThinking] = useState("");
  const [error, setError] = useState<string | null>(null);
  const [showThinking, setShowThinking] = useState(false);
  const [lastResult, setLastResult] = useState<"success" | "error" | null>(null);
  const [lastPayload, setLastPayload] = useState<string | null>(null);
  const textareaRef = useRef<HTMLTextAreaElement>(null);
  const abortControllerRef = useRef<AbortController | null>(null);

  const organization = useOrganization();
  const project = useProject();
  const environment = useEnvironment();

  const resourcePath = `/resources/orgs/${organization.slug}/projects/${project.slug}/env/${environment.slug}/test/ai-generate-payload`;

  const submitGeneration = useCallback(
    async (queryPrompt: string) => {
      if (!queryPrompt.trim() || isLoadingRef.current) return;

      isLoadingRef.current = true;
      setIsLoading(true);
      setThinking("");
      setError(null);
      setShowThinking(true);
      setLastResult(null);
      setLastPayload(null);

      if (abortControllerRef.current) {
        abortControllerRef.current.abort();
      }
      abortControllerRef.current = new AbortController();

      try {
        const formData = new FormData();
        formData.append("prompt", queryPrompt);
        formData.append("taskIdentifier", taskIdentifier);
        formData.append("isAgent", isAgent ? "true" : "false");
        if (payloadSchema) {
          formData.append("payloadSchema", JSON.stringify(payloadSchema));
        }
        const currentPayload = getCurrentPayload?.();
        if (currentPayload) {
          formData.append("currentPayload", currentPayload);
        }

        const response = await fetch(resourcePath, {
          method: "POST",
          body: formData,
          signal: abortControllerRef.current.signal,
        });

        if (!response.ok) {
          const errorData = (await response.json()) as { error?: string };
          setError(errorData.error || "Failed to generate payload");
          setIsLoading(false);
          setLastResult("error");
          return;
        }

        const reader = response.body?.getReader();
        if (!reader) {
          setError("No response stream");
          setIsLoading(false);
          setLastResult("error");
          return;
        }

        const decoder = new TextDecoder();
        let buffer = "";

        while (true) {
          const { done, value } = await reader.read();
          if (done) break;

          buffer += decoder.decode(value, { stream: true });

          const lines = buffer.split("\n\n");
          buffer = lines.pop() || "";

          for (const line of lines) {
            if (line.startsWith("data: ")) {
              try {
                const event = JSON.parse(line.slice(6)) as StreamEventType;
                processStreamEvent(event);
              } catch {
                // Ignore parse errors
              }
            }
          }
        }

        if (buffer.startsWith("data: ")) {
          try {
            const event = JSON.parse(buffer.slice(6)) as StreamEventType;
            processStreamEvent(event);
          } catch {
            // Ignore parse errors
          }
        }
      } catch (err) {
        if (err instanceof Error && err.name === "AbortError") return;
        setError(err instanceof Error ? err.message : "An error occurred");
        setLastResult("error");
      } finally {
        isLoadingRef.current = false;
        setIsLoading(false);
      }
    },
    [resourcePath, taskIdentifier, payloadSchema, getCurrentPayload, isAgent]
  );

  const processStreamEvent = useCallback(
    (event: StreamEventType) => {
      switch (event.type) {
        case "thinking":
          setThinking((prev) => prev + event.content);
          break;
        case "result":
          if (event.success) {
            onPayloadGenerated(event.payload);
            setLastPayload(event.payload);
            setPrompt("");
            setLastResult("success");
          } else {
            setError(event.error);
            setLastResult("error");
          }
          break;
      }
    },
    [onPayloadGenerated]
  );

  const handleSubmit = useCallback(
    (e?: React.FormEvent) => {
      e?.preventDefault();
      submitGeneration(prompt);
    },
    [prompt, submitGeneration]
  );

  useEffect(() => {
    return () => {
      if (abortControllerRef.current) {
        abortControllerRef.current.abort();
      }
    };
  }, []);

  useEffect(() => {
    if (error) {
      const timer = setTimeout(() => setError(null), 15000);
      return () => clearTimeout(timer);
    }
  }, [error]);

  const examplePrompts =
    examplePromptsOverride ??
    (payloadSchema
      ? [
          "Generate a valid payload",
          "Generate a payload with edge cases",
          "Generate a minimal payload with only required fields",
        ]
      : [
          "Generate a simple JSON payload",
          "Generate a payload with nested objects",
          "Generate a payload with an array of items",
        ]);

  return (
    <div className="space-y-0">
      <div
        className="overflow-hidden rounded-md p-px"
        style={{ background: "linear-gradient(to bottom right, #E543FF, #286399)" }}
      >
        <div className="overflow-hidden rounded-md bg-background-bright">
          <div>
            <textarea
              ref={textareaRef}
              name="prompt"
              placeholder={
                placeholder ??
                (payloadSchema
                  ? "e.g. generate a payload for a new user signup"
                  : "e.g. generate a JSON payload with name, email, and age fields")
              }
              value={prompt}
              onChange={(e) => setPrompt(e.target.value)}
              disabled={isLoading}
              rows={5}
              className="focus-custom m-0 min-h-10 w-full resize-none border-0 bg-background-bright px-3 py-2.5 text-sm text-text-bright scrollbar-thin scrollbar-track-transparent scrollbar-thumb-surface-control placeholder:text-text-dimmed focus:border-0 focus:ring-0 disabled:cursor-not-allowed disabled:opacity-50"
              onKeyDown={(e) => {
                if (e.key === "Enter" && !e.shiftKey && prompt.trim() && !isLoading) {
                  e.preventDefault();
                  handleSubmit();
                }
              }}
            />
            <div className="flex justify-end gap-2 px-2 pb-2">
              {isLoading ? (
                <Button
                  type="button"
                  variant="tertiary/small"
                  disabled={true}
                  LeadingIcon={Spinner}
                  className="pl-2"
                  iconSpacing="gap-1.5"
                >
                  Generating…
                </Button>
              ) : (
                <Button
                  type="button"
                  variant="tertiary/small"
                  disabled={!prompt.trim()}
                  className={cn(!prompt.trim() && "opacity-50")}
                  onClick={() => handleSubmit()}
                >
                  {generateButtonLabel}
                </Button>
              )}
            </div>
          </div>
        </div>
      </div>

      {/* Error message */}
      <AnimatePresence>
        {error && (
          <motion.div
            initial={{ opacity: 0, height: 0 }}
            animate={{ opacity: 1, height: "auto" }}
            exit={{ opacity: 0, height: 0 }}
            transition={{ duration: 0.2 }}
            className="overflow-hidden"
          >
            <div className="rounded-md border border-error/30 bg-error/10 px-3 py-2 text-sm text-error">
              {error}
            </div>
          </motion.div>
        )}
      </AnimatePresence>

      {/* Thinking panel */}
      <AnimatePresence>
        {showThinking && thinking && (
          <motion.div
            initial={{ opacity: 0, height: 0 }}
            animate={{ opacity: 1, height: "auto" }}
            exit={{ opacity: 0, height: 0 }}
            transition={{ duration: 0.2 }}
            className="overflow-hidden rounded-b-md border-x border-b border-grid-bright bg-background-bright pb-3"
          >
            <div className="space-y-2 px-2 pt-2">
              <div className="flex items-center justify-between gap-2">
                <div className="flex items-center gap-1.5">
                  {isLoading ? (
                    <Spinner className="size-4" />
                  ) : lastResult === "success" ? (
                    <CheckIcon className="size-4 text-success" />
                  ) : lastResult === "error" ? (
                    <XMarkIcon className="size-4 text-error" />
                  ) : null}
                  <span className="text-xs font-medium text-text-dimmed">
                    {isLoading
                      ? "AI is thinking…"
                      : lastResult === "success"
                        ? "Payload generated"
                        : lastResult === "error"
                          ? "Generation failed"
                          : "AI response"}
                  </span>
                </div>
                <div className="flex items-center gap-0.5">
                  {lastResult === "success" && lastPayload && (
                    <SimpleTooltip
                      asChild
                      side="top"
                      content="Copy"
                      disableHoverableContent
                      button={
                        <button
                          type="button"
                          aria-label="Copy"
                          onClick={() => {
                            if (lastPayload) {
                              void navigator.clipboard.writeText(lastPayload);
                            }
                          }}
                          className="rounded p-1 text-text-dimmed transition-colors hover:bg-background-raised hover:text-text-bright"
                        >
                          <ClipboardIcon className="size-4" />
                        </button>
                      }
                    />
                  )}
                  <SimpleTooltip
                    asChild
                    side="top"
                    content="Dismiss"
                    disableHoverableContent
                    button={
                      <button
                        type="button"
                        aria-label="Dismiss"
                        onClick={() => {
                          if (abortControllerRef.current) {
                            abortControllerRef.current.abort();
                          }
                          setIsLoading(false);
                          setShowThinking(false);
                          setThinking("");
                        }}
                        className="rounded p-1 text-text-dimmed transition-colors hover:bg-background-raised hover:text-text-bright"
                      >
                        <XMarkIcon className="size-4" />
                      </button>
                    }
                  />
                </div>
              </div>
              <div
                className={cn(
                  "streamdown-container max-h-96 overflow-y-auto text-xs text-text-dimmed",
                  "scrollbar-thin scrollbar-track-transparent scrollbar-thumb-surface-control",
                  // Strip Streamdown's code-block chrome
                  "**:data-[streamdown=code-block-header]:hidden",
                  "**:data-[streamdown=code-block]:border-0!",
                  "**:data-[streamdown=code-block]:bg-transparent!",
                  "**:data-[streamdown=code-block]:p-0!",
                  "**:data-[streamdown=code-block]:my-0!",
                  "**:data-[streamdown=code-block]:gap-0!",
                  // Strip the inner code-block-body border (it draws a faint inset frame)
                  "**:data-[streamdown=code-block-body]:border-0!",
                  // Style Streamdown's inner code-block (where the horizontal scrollbar lives)
                  "**:data-[streamdown=code-block-body]:scrollbar-thin",
                  "**:data-[streamdown=code-block-body]:scrollbar-track-transparent",
                  "**:data-[streamdown=code-block-body]:scrollbar-thumb-surface-control"
                )}
              >
                <Suspense fallback={<p className="whitespace-pre-wrap">{thinking}</p>}>
                  <StreamdownRenderer isAnimating={isLoading}>{thinking}</StreamdownRenderer>
                </Suspense>
              </div>
            </div>
          </motion.div>
        )}
      </AnimatePresence>

      {/* Example prompts */}
      <div className="pt-4">
        {showExamplePromptsHeader && (
          <Header3 className="mb-3 text-text-bright">Example prompts</Header3>
        )}
        <div className="flex flex-wrap gap-2">
          {examplePrompts.map((example) => (
            <button
              key={example}
              type="button"
              disabled={isLoading}
              onClick={() => {
                setPrompt(example);
                submitGeneration(example);
              }}
              className="group flex w-fit items-center gap-2 rounded-full border border-dashed border-border-bright px-4 py-2 transition-colors focus-custom hover:border-solid hover:border-indigo-500 focus-visible:rounded-full! disabled:cursor-not-allowed disabled:opacity-50"
            >
              <SparkleListIcon className="size-4 shrink-0 text-text-dimmed transition group-hover:text-indigo-500" />
              <Paragraph
                variant="small"
                className="text-left transition group-hover:text-text-bright"
              >
                {example}
              </Paragraph>
            </button>
          ))}
        </div>
      </div>
    </div>
  );
}
