import React from "react";
import PropTypes from "prop-types";
import styling from "@devcake/styling";
import { FormElement } from "../formElement";
import { Button } from "../../Button";
import { Icon } from "../../Icon";
import { Label } from "../Label";
import { ErrorMessage } from "../Error";

styling.setDefault("components.fileinput.buttons.select", "rounded-l-none");
styling.setDefault("components.fileinput.buttons.select.with-remove", "rounded-r-none");
styling.setDefault("components.fileinput.buttons.remove", "rounded-l-none border-l");
styling.link("components.fileinput.invalid", "components.form.invalid");
styling.link("components.fileinput.icons.select", "components.icons.document-add");
styling.link("components.fileinput.icons.remove", "components.icons.trash");

export type File = {
  name: string;
  data: Blob;
} | { name: null; data: null };
export type FileInputProps = FormElement<File | string> & {
  buttonType?: "primary" | "secondary" | "tertiary",
  buttonColor?: "amber" | "blue" | "cyan" | "emerald" | "fuchsia" | "gray" | "green" | "indigo" | "lightBlue" | "lime" | "orange" | "pink" | "purple" | "red" | "rose" | "teal" | "violet" | "yellow";
}

export const FileInput: React.FunctionComponent<FileInputProps> = ({
                                                                     buttonColor,
                                                                     buttonType,
                                                                     className,
                                                                     disabled,
                                                                     label,
                                                                     nullable,
                                                                     onChange,
                                                                     placeholder,
                                                                     validation,
                                                                     validationMessage,
                                                                     value
                                                                   }) => {
  const ref = React.createRef<HTMLInputElement>();
  const [file, setFile] = React.useState<File>({ name: null, data: null });
  const [error, setError] = React.useState(null);

  const validator = React.useMemo(() => validation instanceof RegExp ? v => validation.test(v) : validation, [validation]);

  const update = React.useCallback((evt: React.ChangeEvent<HTMLInputElement> | null) => {
    setError(null);

    let data = { name: null, data: null };
    if (evt != null) {
      const file = evt.target.files[0];
      if (file != undefined) {
        data = { name: file.name, data: file };
      }
    }

    if (data.name === null && !nullable) {
      setError("This field cannot be empty.");
    }

    if (validator) {
      const ret = validator(data.name);
      if (ret === false) setError(validationMessage);
      if (typeof ret === "string") setError(ret);
    }

    setFile(data);
    if (onChange) onChange(data);
  }, [setFile]);

  React.useEffect(() => {
    let name = typeof value === "object" ? value.name : value;
    setFile({ name, data: null });
  }, [value]);

  return <>
    {label && <Label className="mb-1">{label}</Label>}
    <div className={[
      "select-none flex flex-row",
      className,
      styling.get("components.input.container"),
      styling.get("components.input"),
      disabled ? styling.get("components.input.disabled") : "",
      error ? styling.get("components.fileinput.invalid") : ""
    ].join(" ")}>
      <input
        type="file"
        className="hidden"
        ref={ref}
        onChange={update}
      />
      <span
        className={[
          (!file.name ? "text-gray-500" : ""),
          "px-3 py-2 flex-grow"
        ].join(" ")}
        onClick={() => ref.current.click()}
      >
        {file.name ?? placeholder}
      </span>
      <Button
        color={buttonColor ?? "indigo"}
        type={buttonType}
        className={[
          "flex-grow-0",
          styling.get("components.fileinput.buttons.select"),
          (file.name ? styling.get("components.fileinput.buttons.select.with-remove") : "")
        ].join(" ")}
        onClick={() => ref.current.click()}
      >
        <Icon
          icon={styling.get("components.fileinput.icons.select")}
          className="h-5"
        />
      </Button>
      {file.name && (
        <Button
          color={buttonColor ?? "red"}
          type={buttonType}
          className={[
            `flex-grow-0 border-${buttonColor ?? "red"}-300`,
            styling.get("components.fileinput.buttons.remove")
          ].join(" ")}
          onClick={() => update(null)}
        >
          <Icon
            icon={styling.get("components.fileinput.icons.remove")}
            className="h-5"
          />
        </Button>
      )}
    </div>
    {error && <ErrorMessage>{error}</ErrorMessage>}
  </>;
};

FileInput.propTypes = {
  buttonColor: PropTypes.oneOf(["amber", "blue", "cyan", "emerald", "fuchsia", "gray", "green", "indigo", "lightBlue", "lime", "orange", "pink", "purple", "red", "rose", "teal", "violet", "yellow"]),
  buttonType: PropTypes.oneOf(["primary", "secondary", "tertiary"]),
  className: PropTypes.string,
  disabled: PropTypes.bool,
  label: PropTypes.node,
  nullable: PropTypes.bool,
  onChange: PropTypes.func,
  placeholder: PropTypes.string,
  value: PropTypes.string
};
FileInput.defaultProps = {
  buttonType: "secondary",
  disabled: false,
  nullable: true,
  onChange: () => null,
  placeholder: "Please select a file"
};