import { ParsedRequest, Theme, FileType } from "../api/_lib/types";
const { H, R, copee } = window as any;
let timeout = -1;

interface ImagePreviewProps {
  src: string;
  onclick: () => void;
  onload: () => void;
  onerror: () => void;
  loading: boolean;
}

const ImagePreview = ({
  src,
  onclick,
  onload,
  onerror,
  loading,
}: ImagePreviewProps) => {
  const style = {
    filter: loading ? "blur(5px)" : "",
    opacity: loading ? 0.1 : 1,
  };
  const title = "Click to copy image URL to clipboard";
  return H(
    "a",
    { className: "image-wrapper", href: src, onclick },
    H("img", { src, onload, onerror, style, title })
  );
};

interface DropdownOption {
  text: string;
  value: string;
}

interface DropdownProps {
  options: DropdownOption[];
  value: string;
  onchange: (val: string) => void;
  small: boolean;
}

const Dropdown = ({ options, value, onchange, small }: DropdownProps) => {
  const wrapper = small ? "select-wrapper small" : "select-wrapper";
  const arrow = small ? "select-arrow small" : "select-arrow";
  return H(
    "div",
    { className: wrapper },
    H(
      "select",
      { onchange: (e: any) => onchange(e.target.value) },
      options.map((o) =>
        H("option", { value: o.value, selected: value === o.value }, o.text)
      )
    ),
    H("div", { className: arrow }, "▼")
  );
};

interface TextInputProps {
  value: string;
  oninput: (val: string) => void;
}

const TextInput = ({ value, oninput }: TextInputProps) => {
  return H(
    "div",
    { className: "input-outer-wrapper" },
    H(
      "div",
      { className: "input-inner-wrapper" },
      H("input", {
        type: "text",
        value,
        oninput: (e: any) => oninput(e.target.value),
      })
    )
  );
};

interface ButtonProps {
  label: string;
  onclick: () => void;
}

const Button = ({ label, onclick }: ButtonProps) => {
  return H("button", { onclick }, label);
};

interface FieldProps {
  label: string;
  input: any;
}

const Field = ({ label, input }: FieldProps) => {
  return H(
    "div",
    { className: "field" },
    H(
      "label",
      H("div", { className: "field-label" }, label),
      H("div", { className: "field-value" }, input)
    )
  );
};

interface ToastProps {
  show: boolean;
  message: string;
}

const Toast = ({ show, message }: ToastProps) => {
  const style = { transform: show ? "translate3d(0,-0px,-0px) scale(1)" : "" };
  return H(
    "div",
    { className: "toast-area" },
    H(
      "div",
      { className: "toast-outer", style },
      H(
        "div",
        { className: "toast-inner" },
        H("div", { className: "toast-message" }, message)
      )
    )
  );
};

const themeOptions: DropdownOption[] = [
  { text: "Light", value: "light" },
  { text: "Dark", value: "dark" },
];

const fileTypeOptions: DropdownOption[] = [
  { text: "PNG", value: "png" },
  { text: "JPEG", value: "jpeg" },
];

const fontSizeOptions: DropdownOption[] = Array.from({ length: 10 })
  .map((_, i) => i * 25)
  .filter((n) => n > 0)
  .map((n) => ({ text: n + "px", value: n + "px" }));

const markdownOptions: DropdownOption[] = [
  { text: "Plain Text", value: "0" },
  { text: "Markdown", value: "1" },
];

const imageLightOptions: DropdownOption[] = [
  {
    text: "Manifold",
    value: "https://manifold.markets/logo.png",
  },
  {
    text: "Vercel",
    value:
      "https://assets.vercel.com/image/upload/front/assets/design/vercel-triangle-black.svg",
  },
  {
    text: "Next.js",
    value:
      "https://assets.vercel.com/image/upload/front/assets/design/nextjs-black-logo.svg",
  },
  {
    text: "Hyper",
    value:
      "https://assets.vercel.com/image/upload/front/assets/design/hyper-color-logo.svg",
  },
];

const imageDarkOptions: DropdownOption[] = [
  {
    text: "Manifold",
    value: "https://manifold.markets/logo.png",
  },
  {
    text: "Vercel",
    value:
      "https://assets.vercel.com/image/upload/front/assets/design/vercel-triangle-white.svg",
  },
  {
    text: "Next.js",
    value:
      "https://assets.vercel.com/image/upload/front/assets/design/nextjs-white-logo.svg",
  },
  {
    text: "Hyper",
    value:
      "https://assets.vercel.com/image/upload/front/assets/design/hyper-bw-logo.svg",
  },
];

const widthOptions = [
  { text: "width", value: "auto" },
  { text: "50", value: "50" },
  { text: "100", value: "100" },
  { text: "150", value: "150" },
  { text: "200", value: "200" },
  { text: "250", value: "250" },
  { text: "300", value: "300" },
  { text: "350", value: "350" },
];

const heightOptions = [
  { text: "height", value: "auto" },
  { text: "50", value: "50" },
  { text: "100", value: "100" },
  { text: "150", value: "150" },
  { text: "200", value: "200" },
  { text: "250", value: "250" },
  { text: "300", value: "300" },
  { text: "350", value: "350" },
];

interface AppState extends ParsedRequest {
  loading: boolean;
  showToast: boolean;
  messageToast: string;
  selectedImageIndex: number;
  widths: string[];
  heights: string[];
  overrideUrl: URL | null;
}

type SetState = (state: Partial<AppState>) => void;

const App = (_: any, state: AppState, setState: SetState) => {
  const setLoadingState = (newState: Partial<AppState>) => {
    window.clearTimeout(timeout);
    if (state.overrideUrl && state.overrideUrl !== newState.overrideUrl) {
      newState.overrideUrl = state.overrideUrl;
    }
    if (newState.overrideUrl) {
      timeout = window.setTimeout(() => setState({ overrideUrl: null }), 200);
    }

    setState({ ...newState, loading: true });
  };
  const {
    fileType = "png",
    fontSize = "100px",
    theme = "light",
    md = true,
    text = "**Hello** World",
    images = [imageLightOptions[0].value],
    widths = [],
    heights = [],
    showToast = false,
    messageToast = "",
    loading = true,
    selectedImageIndex = 0,
    overrideUrl = null,
  } = state;
  const mdValue = md ? "1" : "0";
  const imageOptions = theme === "light" ? imageLightOptions : imageDarkOptions;
  const url = new URL(window.location.origin);
  url.pathname = `${encodeURIComponent(text)}.${fileType}`;
  url.searchParams.append("theme", theme);
  url.searchParams.append("md", mdValue);
  url.searchParams.append("fontSize", fontSize);
  for (let image of images) {
    url.searchParams.append("images", image);
  }
  for (let width of widths) {
    url.searchParams.append("widths", width);
  }
  for (let height of heights) {
    url.searchParams.append("heights", height);
  }

  return H(
    "div",
    { className: "split" },
    H(
      "div",
      { className: "pull-left" },
      H(
        "div",
        H(Field, {
          label: "Theme",
          input: H(Dropdown, {
            options: themeOptions,
            value: theme,
            onchange: (val: Theme) => {
              const options =
                val === "light" ? imageLightOptions : imageDarkOptions;
              let clone = [...images];
              clone[0] = options[selectedImageIndex].value;
              setLoadingState({ theme: val, images: clone });
            },
          }),
        }),
        H(Field, {
          label: "File Type",
          input: H(Dropdown, {
            options: fileTypeOptions,
            value: fileType,
            onchange: (val: FileType) => setLoadingState({ fileType: val }),
          }),
        }),
        H(Field, {
          label: "Font Size",
          input: H(Dropdown, {
            options: fontSizeOptions,
            value: fontSize,
            onchange: (val: string) => setLoadingState({ fontSize: val }),
          }),
        }),
        H(Field, {
          label: "Text Type",
          input: H(Dropdown, {
            options: markdownOptions,
            value: mdValue,
            onchange: (val: string) => setLoadingState({ md: val === "1" }),
          }),
        }),
        H(Field, {
          label: "Text Input",
          input: H(TextInput, {
            value: text,
            oninput: (val: string) => {
              console.log("oninput " + val);
              setLoadingState({ text: val, overrideUrl: url });
            },
          }),
        }),
        H(Field, {
          label: "Image 1",
          input: H(
            "div",
            H(Dropdown, {
              options: imageOptions,
              value: imageOptions[selectedImageIndex].value,
              onchange: (val: string) => {
                let clone = [...images];
                clone[0] = val;
                const selected = imageOptions.map((o) => o.value).indexOf(val);
                setLoadingState({
                  images: clone,
                  selectedImageIndex: selected,
                });
              },
            }),
            H(
              "div",
              { className: "field-flex" },
              H(Dropdown, {
                options: widthOptions,
                value: widths[0],
                small: true,
                onchange: (val: string) => {
                  let clone = [...widths];
                  clone[0] = val;
                  setLoadingState({ widths: clone });
                },
              }),
              H(Dropdown, {
                options: heightOptions,
                value: heights[0],
                small: true,
                onchange: (val: string) => {
                  let clone = [...heights];
                  clone[0] = val;
                  setLoadingState({ heights: clone });
                },
              })
            )
          ),
        }),
        ...images.slice(1).map((image, i) =>
          H(Field, {
            label: `Image ${i + 2}`,
            input: H(
              "div",
              H(TextInput, {
                value: image,
                oninput: (val: string) => {
                  let clone = [...images];
                  clone[i + 1] = val;
                  setLoadingState({ images: clone, overrideUrl: url });
                },
              }),
              H(
                "div",
                { className: "field-flex" },
                H(Dropdown, {
                  options: widthOptions,
                  value: widths[i + 1],
                  small: true,
                  onchange: (val: string) => {
                    let clone = [...widths];
                    clone[i + 1] = val;
                    setLoadingState({ widths: clone });
                  },
                }),
                H(Dropdown, {
                  options: heightOptions,
                  value: heights[i + 1],
                  small: true,
                  onchange: (val: string) => {
                    let clone = [...heights];
                    clone[i + 1] = val;
                    setLoadingState({ heights: clone });
                  },
                })
              ),
              H(
                "div",
                { className: "field-flex" },
                H(Button, {
                  label: `Remove Image ${i + 2}`,
                  onclick: (e: MouseEvent) => {
                    e.preventDefault();
                    const filter = (arr: any[]) =>
                      [...arr].filter((_, n) => n !== i + 1);
                    const imagesClone = filter(images);
                    const widthsClone = filter(widths);
                    const heightsClone = filter(heights);
                    setLoadingState({
                      images: imagesClone,
                      widths: widthsClone,
                      heights: heightsClone,
                    });
                  },
                })
              )
            ),
          })
        ),
        H(Field, {
          label: `Image ${images.length + 1}`,
          input: H(Button, {
            label: `Add Image ${images.length + 1}`,
            onclick: () => {
              const nextImage =
                images.length === 1
                  ? "https://cdn.jsdelivr.net/gh/remojansen/logo.ts@master/ts.svg"
                  : "";
              setLoadingState({ images: [...images, nextImage] });
            },
          }),
        })
      )
    ),
    H(
      "div",
      { className: "pull-right" },
      H(ImagePreview, {
        src: overrideUrl ? overrideUrl.href : url.href,
        loading: loading,
        onload: () => setState({ loading: false }),
        onerror: () => {
          setState({
            showToast: true,
            messageToast: "Oops, an error occurred",
          });
          setTimeout(() => setState({ showToast: false }), 2000);
        },
        onclick: (e: Event) => {
          e.preventDefault();
          const success = copee.toClipboard(url.href);
          if (success) {
            setState({
              showToast: true,
              messageToast: "Copied image URL to clipboard",
            });
            setTimeout(() => setState({ showToast: false }), 3000);
          } else {
            window.open(url.href, "_blank");
          }
          return false;
        },
      })
    ),
    H(Toast, {
      message: messageToast,
      show: showToast,
    })
  );
};

R(H(App), document.getElementById("app"));