/* eslint-disable @next/next/no-img-element */
"use client";

import { UserInfo } from "@/ldap";
import React, { HTMLInputTypeAttribute, InputHTMLAttributes } from "react";
import styles from "./AboutMe.module.css";

const fallbackAvatar = "https://i.clong.biz/i/oc4zjlqr.png";

type UpdateResponse = {
  ok: boolean;
  error?: string;
};

// TODO skip do your magic
type InputProps = {
  label: string;
  name: string;
  type: HTMLInputTypeAttribute;
  error?: string;
  displayImage?: string;
} & InputHTMLAttributes<HTMLInputElement>;

const Input = React.forwardRef<HTMLInputElement, InputProps>((props, ref) => {
  // get console to shut up
  const inputProps = { ...props };
  delete inputProps.displayImage;

  return (
    <div className={styles.formRow}>
      <label htmlFor={props.id}>{props.label}</label>

      {props.displayImage && (
        <img
          src={props.displayImage}
          className={styles.avatar}
          alt={"Your avatar"}
          width="50px"
          height="50px"
        />
      )}

      <div className={styles.formVert}>
        <input {...inputProps} ref={ref} className={styles.fancyInput} />

        {props.error != null && <p className={styles.error}>{props.error}</p>}
      </div>
    </div>
  );
});
Input.displayName = "Input";

async function fileAsBase64(f: File) {
  const reader = new FileReader();
  reader.readAsArrayBuffer(f);
  return new Promise<string>((resolve, reject) => {
    reader.onload = () => {
      const result = reader.result as ArrayBuffer;
      const buffer = Buffer.from(result);
      resolve(buffer.toString("base64"));
    };
    reader.onerror = () => reject(reader.error);
  });
}

export default function AboutMe({ info }: { info: UserInfo }) {
  const displayNameRef = React.useRef<HTMLInputElement>(null);
  const emailRef = React.useRef<HTMLInputElement>(null);
  const avatarRef = React.useRef<HTMLInputElement>(null);
  const submitRef = React.useRef<HTMLInputElement>(null);

  const [avatar, setAvatar] = React.useState<string | null>(
    info.avatar ?? null
  );

  const currentPasswordRef = React.useRef<HTMLInputElement>(null);
  const newPasswordRef = React.useRef<HTMLInputElement>(null);
  const confirmPasswordRef = React.useRef<HTMLInputElement>(null);
  const submitPasswordRef = React.useRef<HTMLInputElement>(null);

  const [incorrectPassword, setIncorrectPassword] = React.useState(false);
  const [passwordMismatch, setPasswordMismatch] = React.useState(false);
  const [avatarBig, setAvatarBig] = React.useState(false);

  return (
    <div className={styles.content}>
      <h2 className={styles.header}>User information</h2>
      <form
        onSubmit={async (e) => {
          e.preventDefault();

          // turn the data uri into just base64
          const avatarChanged = avatar !== null && avatar !== info.avatar;
          const avatarData = avatarChanged ? avatar?.split(",")[1] : null;

          submitRef.current!.disabled = true;
          const req = await fetch("/api/update", {
            method: "POST",
            headers: {
              "Content-Type": "application/json"
            },
            body: JSON.stringify({
              displayName: displayNameRef.current?.value,
              email: emailRef.current?.value,
              avatar: avatarData
            })
          });
          submitRef.current!.disabled = false;

          try {
            const res: UpdateResponse = await req.json();

            if (!res.ok && res.error !== null) {
              switch (res.error) {
                case "avatarBig":
                  setAvatarBig(true);
                  break;
              }
            }
          } catch {
            console.error(req);
          }
        }}
      >
        <Input
          type="text"
          name="username"
          label="Username"
          defaultValue={info.username}
          disabled
          title="You can't change your username."
        />
        <Input
          type="text"
          name="display-name"
          label="Display name"
          defaultValue={info.displayName}
          ref={displayNameRef}
        />
        <Input
          type="email"
          name="email"
          label="Email"
          defaultValue={info.email}
          ref={emailRef}
        />

        {/* why, html gods, why? */}
        <input
          type="file"
          name="avatar"
          accept="image/png, image/jpeg"
          ref={avatarRef}
          style={{ display: "none" }}
        />

        <Input
          type="button"
          value="Choose file"
          name="avatar"
          label="Avatar"
          accept="image/png, image/jpeg"
          error={avatarBig ? "Avatar is too big." : undefined}
          onClick={() => {
            avatarRef.current?.click();

            const eventListener = async () => {
              avatarRef.current?.removeEventListener("change", eventListener);

              const file = avatarRef.current?.files?.[0];
              if (file == null) return;

              if (file.size > 1_000_000) {
                setAvatarBig(true);
                return;
              } else {
                setAvatarBig(false);
              }

              const b64 = await fileAsBase64(file);
              setAvatar(`data:${file.type};base64,${b64}`);
            };

            avatarRef.current?.addEventListener("change", eventListener);
          }}
          displayImage={avatar ?? fallbackAvatar}
        />

        <div className={styles.formRow}>
          <input
            type="submit"
            value="Save"
            ref={submitRef}
            className={styles.fancyInput}
          />
        </div>
      </form>

      <hr className={styles.divider} />

      <h2 className={styles.header}>Change password</h2>
      <form
        onSubmit={async (e) => {
          e.preventDefault();
          setIncorrectPassword(false);
          setPasswordMismatch(false);

          if (
            newPasswordRef.current?.value !== confirmPasswordRef.current?.value
          ) {
            setPasswordMismatch(true);
            return;
          }

          submitPasswordRef.current!.disabled = true;
          const req = await fetch("/api/changePassword", {
            method: "POST",
            headers: {
              "Content-Type": "application/json"
            },
            body: JSON.stringify({
              currentPassword: currentPasswordRef.current?.value,
              newPassword: newPasswordRef.current?.value
            })
          });
          submitPasswordRef.current!.disabled = false;

          try {
            const res: UpdateResponse = await req.json();

            if (!res.ok && res.error !== null) {
              switch (res.error) {
                case "incorrectPassword":
                  setIncorrectPassword(true);
                  break;
              }
            }
          } catch {
            console.error(req);
          }
        }}
      >
        <Input
          type="password"
          name="current-password"
          label="Current"
          minLength={12}
          required
          ref={currentPasswordRef}
          error={incorrectPassword ? "Incorrect password." : undefined}
        />

        <Input
          type="password"
          name="new-password"
          label="New"
          minLength={12}
          required
          ref={newPasswordRef}
        />

        <Input
          type="password"
          name="confirm-password"
          label="Confirm"
          ref={confirmPasswordRef}
          minLength={12}
          required
          error={passwordMismatch ? "Passwords do not match." : undefined}
        />

        <div className={styles.formRow}>
          <input
            type="submit"
            value="Change password"
            ref={submitPasswordRef}
            className={styles.fancyInput}
          />
        </div>
      </form>
    </div>
  );
}