import { Field, Fieldset, Label, Legend } from "@catalyst/fieldset";
import { Radio, RadioField, RadioGroup } from "@catalyst/radio";
import React, { useRef, useState } from "react";
import { Input } from "@catalyst/input";
import { Button } from "@catalyst/button";
import { enqueueSnackbar } from "notistack";
import { useNavigate } from "@tanstack/react-router";
import { CloudArrowUpIcon, DocumentCheckIcon } from "@heroicons/react/24/solid";
import { XMarkIcon } from "@heroicons/react/24/outline";
import { useCreateConnection, useCreateFileConnection } from "@/hooks";
import { DatabaseFileType } from "@components/Library/types";

function classNames(...classes: string[]) {
  return classes.filter(Boolean).join(" ");
}

const FileDragAndDrop = ({
  currentFile,
  setFile,
  fileTypeLabel,
}: {
  currentFile: File | undefined;
  setFile: (file: File | undefined) => void;
  fileTypeLabel: string;
}) => {
  const [dragActive, setDragActive] = useState<boolean>(false);
  const fileInputRef = useRef<HTMLInputElement>(null);

  const handleFileClick = () => {
    if (fileInputRef.current) {
      fileInputRef.current.click();
    }
  };

  const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    const files = event.target.files;
    if (files && files.length > 0) {
      setFile(files[0]);
    }
  };

  const handleDrop = (event: React.DragEvent<HTMLDivElement>) => {
    event.preventDefault();
    event.stopPropagation();
    setDragActive(false);
    if (event.dataTransfer.files && event.dataTransfer.files[0]) {
      setFile(event.dataTransfer.files[0]);
    }
  };
  function handleDragLeave(event: React.DragEvent<HTMLDivElement>) {
    event.preventDefault();
    event.stopPropagation();
    setDragActive(false);
  }
  function handleDragOver(event: React.DragEvent<HTMLDivElement>) {
    event.preventDefault();
    event.stopPropagation();
    setDragActive(true);
  }

  function handleDragEnter(event: React.DragEvent<HTMLDivElement>) {
    event.preventDefault();
    event.stopPropagation();
    setDragActive(true);
  }
  return (
    <div
      onDrop={handleDrop}
      onDragEnter={handleDragEnter}
      onDragLeave={handleDragLeave}
      onDragOver={handleDragOver}
      className={classNames(
        "mt-2 flex justify-center rounded-lg border border-dashed border-white/60 px-6 py-10",
        dragActive ? "bg-gray-700" : ""
      )}
    >
      <div className={classNames(currentFile ? "" : "hidden", "text-center")}>
        <div className="relative inline-block">
          <DocumentCheckIcon
            className="h-12 w-12 text-gray-300"
            aria-hidden="true"
          />
          <div
            onClick={() => setFile(undefined)}
            className="absolute -right-1 -top-1 cursor-pointer block h-3 w-3 rounded-full bg-red-500 ring-4 ring-red-500"
          >
            <XMarkIcon
              className="h-3 w-3 text-white [&>path]:stroke-[4]"
              aria-hidden="true"
            />
          </div>
        </div>
        <p className="mt-2 text-sm leading-6 text-gray-400">
          {currentFile && currentFile.name}
        </p>
      </div>
      <div className={classNames(currentFile ? "hidden" : "", "text-center")}>
        <CloudArrowUpIcon
          onClick={handleFileClick}
          className="cursor-pointer mx-auto h-12 w-12 text-gray-300"
          aria-hidden="true"
        />
        <div className="mt-4 flex text-sm leading-6 text-gray-400 justify-center">
          <label
            htmlFor="file-upload"
            className="px-1 relative cursor-pointer rounded-md bg-gray-900 font-semibold text-white focus-within:outline-none focus-within:ring-2 focus-within:ring-indigo-600 focus-within:ring-offset-2 focus-within:ring-offset-gray-900 hover:text-indigo-500"
          >
            <span>Upload a file</span>
            {/** Set a key so that the input is re-rendered and cleared when the file is removed */}
            <input
              ref={fileInputRef}
              id="file-upload"
              name="file-upload"
              type="file"
              className="sr-only"
              onChange={handleFileChange}
              key={currentFile?.name}
            />
          </label>
          <p>or drag and drop</p>
        </div>
        <p className="text-xs leading-5 text-gray-400 px-12 mt-4">
          Creates a copy of your {fileTypeLabel} in DataLine. Changes you make
          to the file will not be accessible to DataLine as it will work on the
          copy you upload.
        </p>
      </div>
    </div>
  );
};

type RadioValue = DatabaseFileType | "database" | null;
const fileTypeLabelMap: { [K in DatabaseFileType]: string } = {
  sqlite: "SQLite data file",
  csv: "CSV file",
  sas7bdat: "sas7bdat file",
  excel: "Excel file",
};

const ConnectionCreator = ({ name = null }: { name: string | null }) => {
  const [selectedRadio, setSelectedRadio] = useState<RadioValue>(null);
  const [dsn, setDsn] = useState<string | null>(null);
  const [file, setFile] = useState<File>();
  const { mutate: createConnection, isPending } = useCreateConnection();
  const { mutate: createFileConnection, isPending: isFilePending } =
    useCreateFileConnection();

  const navigate = useNavigate();

  const handleCustomCreate = async () => {
    // Call api with name and dsn
    if (!name || !dsn) {
      enqueueSnackbar({
        variant: "info",
        message: "Please enter a name and dsn for this connection",
      });
      return;
    }
    createConnection(
      { dsn, name, isSample: false },
      {
        onSuccess: () => {
          enqueueSnackbar({
            variant: "success",
            message: "Connection created",
          });
          navigate({ to: "/" });
        },
      }
    );
  };

  const handleFileCreate = async (type: DatabaseFileType) => {
    if (!file) {
      enqueueSnackbar({
        variant: "info",
        message: "Please add a file",
      });
      return;
    }

    if (!name) {
      enqueueSnackbar({
        variant: "info",
        message: "Please add a name",
      });
      return;
    }

    // Limit file size to 500MB
    if (file.size > 1024 * 1024 * 500) {
      enqueueSnackbar({
        variant: "info",
        message: "File size exceeds 500MB limit",
      });
      return;
    }

    createFileConnection(
      { file, name, type },
      {
        onSuccess: () => {
          enqueueSnackbar({
            variant: "success",
            message: "Connection created",
          });
          navigate({ to: "/" });
        },
      }
    );
  };

  return (
    <>
      <Fieldset>
        <Legend>Create a custom connection</Legend>
        <RadioGroup
          defaultValue=""
          onChange={(selection: string) =>
            setSelectedRadio(selection as RadioValue)
          }
        >
          <RadioField>
            <Radio value="database" color="white" />
            <Label className="cursor-pointer">
              Postgres, MySQL, Snowflake, or MS SQL Server connection string
            </Label>
          </RadioField>
          <RadioField>
            <Radio value="sqlite" color="white" />
            <Label className="cursor-pointer">SQLite file</Label>
          </RadioField>
          <RadioField>
            <Radio value="csv" color="white" />
            <Label className="cursor-pointer">CSV file</Label>
          </RadioField>
          <RadioField>
            <Radio value="excel" color="white" />
            <Label className="cursor-pointer">Excel file</Label>
          </RadioField>
          <RadioField>
            <Radio value="sas7bdat" color="white" />
            <Label className="cursor-pointer">sas7bdat file</Label>
          </RadioField>
        </RadioGroup>
      </Fieldset>
      <div className="mt-10 max-w-2xl">
        {selectedRadio === "database" ? (
          <div>
            <Field>
              <Label>Connection DSN</Label>
              <Input
                type="text"
                placeholder="postgres://myuser:mypassword@localhost:5432/mydatabase"
                onChange={(e) => setDsn(e.target.value)}
              />
            </Field>
            <Button
              className="cursor-pointer mt-4"
              onClick={handleCustomCreate}
              disabled={isPending}
            >
              Create connection
            </Button>
          </div>
        ) : (
          selectedRadio && (
            <div>
              <Field>
                <Label>{fileTypeLabelMap[selectedRadio]}</Label>
                <FileDragAndDrop
                  setFile={setFile}
                  currentFile={file}
                  fileTypeLabel={fileTypeLabelMap[selectedRadio]}
                />
              </Field>
              <Button
                className="cursor-pointer mt-4"
                onClick={() => handleFileCreate(selectedRadio)}
                disabled={isFilePending}
              >
                Create connection
              </Button>
            </div>
          )
        )}
      </div>
    </>
  );
};

export default ConnectionCreator;
