import { useMutation, useQuery } from "@tanstack/react-query";
import Ansi from "ansi-to-react";
import { createId } from "@paralleldrive/cuid2";
import { useProjectContext } from "../context/project";
import { api } from "~/lib/api";
import Spinner from "~/components/ui/spinner";
import { useEffect, useState } from "react";
import { BASE_URL } from "~/lib/consts";
import { useSSE } from "~/hooks/useSSE";
import Divider from "~/components/ui/divider";
import { Button } from "~/components/ui/button";
import { FaCopy, FaExternalLinkAlt, FaTimes } from "react-icons/fa";
import ActionButton from "~/components/ui/action-button";
import { copy, getUrl } from "~/lib/utils";

const APIManager = () => {
  const { project } = useProjectContext();

  const stats = useQuery({
    queryKey: ["sandbox/stats", project.slug],
    queryFn: () => api(`/sandbox/${project.slug}/stats`),
    refetchInterval: 5000,
    retry: false,
  });

  const start = useMutation({
    mutationFn: () => api(`/sandbox/${project.slug}/start`, { method: "POST" }),
    onSuccess: () => stats.refetch(),
  });

  useEffect(() => {
    if (stats.error && (stats.error as any).code === 404 && start.isIdle) {
      start.mutate();
    }
  }, [stats.error, start.isIdle]);

  const onRetry = () => {
    if (start.isError) {
      start.mutate();
    } else if (!stats.data) {
      stats.refetch();
    }
  };

  if (stats.isLoading || start.isPending) {
    return (
      <div className="p-8 h-full flex flex-col items-center justify-center">
        <Spinner />
        <p>
          {start.isPending
            ? "Starting up development sandbox..."
            : "Please wait..."}
        </p>
      </div>
    );
  }

  if (!stats.data || start.isError) {
    return (
      <div className="p-8 h-full flex flex-col items-center justify-center">
        <p>Cannot load dev sandbox :(</p>
        {start.error?.message ? (
          <p className="text-sm mt-2">{start.error.message}</p>
        ) : null}

        <Button onClick={onRetry} className="mt-4">
          Retry
        </Button>
      </div>
    );
  }

  return (
    <div className="p-4 pt-2 h-full flex flex-col">
      <div className="flex gap-4 items-start">
        <Stats data={stats.data.result} />
        <Actions stats={stats} />
      </div>
      <Divider className="my-2" />

      <p className="text-sm mb-1">Output:</p>
      <Logs />
    </div>
  );
};

const Actions = ({ stats }: any) => {
  const { project } = useProjectContext();

  const restart = useMutation({
    mutationFn: () => {
      return api(`/sandbox/${project.slug}/restart`, { method: "POST" });
    },
    onSuccess: () => stats.refetch(),
  });
  const { address } = stats.data?.result;

  return (
    <div className="flex items-center gap-2">
      <Button
        onClick={() => restart.mutate()}
        isLoading={restart.isPending}
        size="sm"
        className="h-8"
      >
        Restart
      </Button>
      <ActionButton
        icon={FaCopy}
        variant="outline"
        size="md"
        onClick={() => copy(address)}
      />
      <ActionButton
        icon={FaExternalLinkAlt}
        variant="outline"
        size="md"
        onClick={() => window.open(address, "_blank")}
      />
    </div>
  );
};

const Stats = ({ data }: any) => {
  const { cpu, mem, memUsage, network, status, address } = data;
  const [memUsed, memTotal] = memUsage || [];

  return (
    <div className="flex flex-col text-sm flex-1">
      <p>Status: {status}</p>
      <p>Address: {address}</p>
      <p>CPU: {cpu}%</p>
      <p>
        Memory: {memUsed != null ? `${memUsed} / ${memTotal} (${mem}%)` : "-"}
      </p>
    </div>
  );
};

const Logs = () => {
  const { project } = useProjectContext();
  const url = BASE_URL + `/api/sandbox/${project.slug}/logs`;
  const [logs, setLogs] = useState<{ log: string; time: number; id: string }[]>(
    []
  );

  function onData(data: any) {
    setLogs((l) => [
      { ...data, log: data.log.replace(/[^\x00-\x7F]/g, ""), id: createId() },
      ...l,
    ]);
  }

  useSSE(url, onData);
  useEffect(() => {
    setLogs([]);
  }, [url]);

  return (
    <div className="w-full flex-1 shrink-0 bg-gray-900 p-4 overflow-y-auto flex flex-col-reverse gap-2 rounded-lg text-sm relative">
      <ActionButton
        icon={FaTimes}
        className="absolute top-1 right-2"
        onClick={() => setLogs([])}
      />

      {logs.map((log) => (
        <div
          key={log.id}
          className="border-t last:border-t-0 border-t-gray-800 pt-2"
        >
          {log.log.split("\n").map((line, idx) => (
            <Ansi key={idx} className="block">
              {line}
            </Ansi>
          ))}
        </div>
      ))}
    </div>
  );
};

export default APIManager;