import {
  Button,
  CircularProgress,
  CircularProgressLabel,
  Container,
  FormControl,
  FormLabel,
  Heading,
  HStack,
  Input,
  Link,
  Text,
  useToast,
} from "@chakra-ui/react";
import { useEffect, useState } from "react";
import { useLoaderData } from "@remix-run/react";
import Success from "../../components/Success.js";

export async function loader({
  context,
}: {
  context: RequestContext;
}): Promise<{ logged_in: boolean; site_key: string }> {
  return {
    logged_in: Boolean(context.data.current_user),
    site_key: context.env.TURNSTILE_SITEKEY,
  };
}

export function meta() {
  return [
    {
      title: "Report an Exploiter - Car Crushers",
    },
  ];
}

export default function () {
  const [fileProgress, setFileProgress] = useState(0);
  const [showSuccess, setShowSuccess] = useState(false);
  const [supportsRequestStreams, setSupportsRequestStreams] = useState(false);
  const toast = useToast();
  const [uploading, setUploading] = useState(false);
  const [turnstileToken, setTurnstileToken] = useState("");
  const fileTypes: { [k: string]: string } = {
    gif: "image/gif",
    heic: "image/heic",
    heif: "image/heif",
    jfif: "image/jpeg",
    jpeg: "image/jpeg",
    jpg: "image/jpg",
    m4v: "video/x-m4v",
    mkv: "video/x-matroska",
    mov: "video/quicktime",
    mp4: "video/mp4",
    png: "image/png",
    webp: "image/webp",
    webm: "video/webm",
    wmv: "video/x-ms-wmv",
  };

  useEffect(() => {
    if (sessionStorage.getItem("REPORT_SUCCESS")) {
      sessionStorage.removeItem("REPORT_SUCCESS");
      return setShowSuccess(true);
    }

    setSupportsRequestStreams(
      (() => {
        let duplexAccessed = false;

        const hasContentType = new Request("", {
          body: new ReadableStream(),
          method: "POST",
          // @ts-ignore
          get duplex() {
            duplexAccessed = true;
            return "half";
          },
        }).headers.has("Content-Type");

        return duplexAccessed && !hasContentType;
      })()
    );
  }, []);

  const { logged_in, site_key } = useLoaderData<typeof loader>();

  function setToken(token: string) {
    setTurnstileToken(token);
  }

  async function submit() {
    const usernames = (
      document.getElementById("usernames") as HTMLInputElement
    ).value
      .replaceAll(" ", "")
      .split(",");
    const file = (
      document.getElementById("evidence") as HTMLInputElement
    ).files?.item(0);

    if (!usernames.length)
      return toast({
        description: "Must provide at least one username",
        isClosable: true,
        status: "error",
        title: "Error",
      });

    if (!file)
      return toast({
        description: "Must attach a file",
        isClosable: true,
        status: "error",
        title: "Error",
      });

    if (usernames.length > 20)
      return toast({
        description: "Only up to twenty users can be reported at a time",
        isClosable: true,
        status: "error",
        title: "Too Many Usernames",
      });

    const submitReq = await fetch("/api/reports/submit", {
      body: JSON.stringify({
        filename: file.name,
        filesize: file.size,
        usernames,
      }),
      headers: {
        "content-type": "application/json",
      },
      method: "POST",
    });

    if (!submitReq.ok) {
      if (!logged_in) {
        try {
          // @ts-expect-error
          turnstile.reset();
        } catch {}
      }

      return toast({
        description: ((await submitReq.json()) as { error: string }).error,
        isClosable: true,
        status: "error",
        title: "Error",
      });
    }

    const { id, upload_url }: { id: string; upload_url: string } =
      await submitReq.json();

    setUploading(true);
    const reader = file.stream().getReader();
    let bytesRead = 0;

    const uploadReq = await fetch(upload_url, {
      body: supportsRequestStreams
        ? new ReadableStream({
            async pull(controller) {
              const chunk = await reader.read();

              if (chunk.done) {
                controller.close();
                setUploading(false);
                return;
              }

              controller.enqueue(chunk.value);
              bytesRead += chunk.value.length;
              setFileProgress(Math.floor((bytesRead / file.size) * 100));
            },
          })
        : file,
      // @ts-expect-error
      duplex: supportsRequestStreams ? "half" : undefined,
      headers: {
        "content-type":
          file.type ||
          fileTypes[file.name.split(".")[file.name.split(".").length - 1]],
      },
      method: "PUT",
    }).catch(console.error);

    if (!uploadReq?.ok) {
      await fetch("/api/reports/recall", {
        body: JSON.stringify({ id }),
        headers: {
          "content-type": "application/json",
        },
        method: "POST",
      });

      return toast({
        description: "Failed to upload file",
        isClosable: true,
        status: "error",
        title: "Error",
      });
    }

    await fetch("/api/reports/complete", {
      body: JSON.stringify({ id }),
      headers: {
        "content-type": "application/json",
      },
      method: "POST",
    });

    sessionStorage.setItem("REPORT_SUCCESS", "1");
  }

  useEffect(() => {
    if (logged_in) return;

    const script = document.createElement("script");
    script.async = true;
    script.defer = true;
    script.src = "https://challenges.cloudflare.com/turnstile/v0/api.js";

    document.body.appendChild(script);
  }, [logged_in]);

  return showSuccess ? (
    <Success
      heading="Report Submitted"
      message="We will review it as soon as possible."
    />
  ) : (
    <>
      <Container maxW="container.md" pt="4vh" textAlign="start">
        <Heading mb="4vh">Report an Exploiter</Heading>
        <br />
        <FormControl isRequired>
          <FormLabel>
            Username(s) - To specify more than one, provide a comma-delimited
            list (User1, User2, User3...)
          </FormLabel>
          <Input id="usernames" placeholder="builderman" />
        </FormControl>
        <br />
        <FormControl isRequired>
          <FormLabel>Your Evidence (Max Size: 512MB)</FormLabel>
          <Button
            colorScheme="blue"
            mr="8px"
            onClick={() => document.getElementById("evidence")?.click()}
          >
            Select File
          </Button>
          <input id="evidence" type="file" />
        </FormControl>
        <br />
        <div
          className="cf-turnstile"
          data-callback="setToken"
          data-sitekey={site_key}
        ></div>
        <br />
        <Text>
          By submitting this form, you agree to the{" "}
          <Link color="#646cff" href="/terms">
            Terms of Service
          </Link>{" "}
          and{" "}
          <Link color="#646cff" href="/privacy">
            Privacy Policy
          </Link>
          .
        </Text>
        <br />
        <HStack>
          <Button
            disabled={uploading}
            mr="8px"
            onClick={async () => await submit()}
          >
            Submit
          </Button>
          <CircularProgress
            display={uploading ? "" : "none"}
            isIndeterminate={!supportsRequestStreams}
            value={supportsRequestStreams ? fileProgress : undefined}
          >
            {supportsRequestStreams ? (
              <CircularProgressLabel>{fileProgress}%</CircularProgressLabel>
            ) : null}
          </CircularProgress>
        </HStack>
      </Container>
    </>
  );
}