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

import { UserInfo } from "@/ldap";
import React from "react";
import styles from "./AboutMe.module.css";
import AvatarChanger from "@/components/AvatarChanger";
import Input from "@/components/Input";
import { Form, Formik, FormikHelpers } from "formik";
import {
  AboutMeFormValues,
  PasswordUpdateFormValues,
  aboutMeSchema,
  passwordUpdateSchema
} from "@/schemas";
import PrettyForm from "@/components/PrettyForm";
import Toast from "@/components/Toast";
import { AuthProviderState } from "@/auth/AuthProvider";
import { exec } from "child_process";

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

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);
  });
}

function AuthProviderButton({ provider }: { provider: AuthProviderState }) {
  // bullshit hack
  const holdTime = provider.connected ? 3000 : 0;
  const interval = React.useRef<NodeJS.Timeout | null>();
  const inputRef = React.useRef<HTMLInputElement>(null);

  const execute = async () => {
    const name = provider.name.toLowerCase();
    if (!provider.connected) {
      window.location.href = `/oauth/${name}/login`;
    } else {
      await fetch(`/api/unlink?provider=${name}`, { method: "POST" });
      window.location.reload();
    }
  };

  const mouseDown = () => {
    interval.current = setTimeout(execute, holdTime);
  };

  const mouseUp = () => {
    if (interval.current) clearTimeout(interval.current);
  };

  return (
    <input
      type="submit"
      className={
        styles.fancyInput + " " + (provider.connected ? styles.progress : "")
      }
      onMouseDown={mouseDown}
      onMouseUp={mouseUp}
      value={provider.connected ? "Disconnect" : "Connect"}
      ref={inputRef}
    />
  );
}

function AuthProviderEntry({ provider }: { provider: AuthProviderState }) {
  return (
    <>
      <p>
        {provider.name}:{" "}
        {provider.connected ? provider.username : "Not connected"}
      </p>

      <AuthProviderButton provider={provider} />
    </>
  );
}

export default function AboutMe({
  info,
  providers
}: {
  info: UserInfo;
  providers: AuthProviderState[];
}) {
  const [globalError, setGlobalError] = React.useState<string | null>(null);
  const [madeProfileChanges, setMadeChanges] = React.useState(false);
  const [madePasswordChanges, setMadePasswordChanges] = React.useState(false);

  const initialValues: AboutMeFormValues = {
    username: info.username,
    displayName: info.displayName,
    email: info.email,
    avatar: info.avatar
  };

  async function handleFormSubmit(
    { displayName, email, avatar }: AboutMeFormValues,
    { setSubmitting }: FormikHelpers<AboutMeFormValues>
  ) {
    setMadeChanges(false);
    setSubmitting(true);
    const req = await fetch("/api/update", {
      method: "POST",
      headers: {
        "Content-Type": "application/json"
      },
      body: JSON.stringify({
        displayName,
        email,
        avatar: avatar != null ? avatar.split(",")[1] : null
      })
    });
    setSubmitting(false);

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

      if (!res.ok && res.error !== null) {
        switch (res.error) {
          case "avatarBig":
            break;
        }
      }
      setMadeChanges(true);
    } catch {
      console.error(req);
    }
  }

  const [passwordError, setPasswordError] = React.useState<string | null>(null);
  const initialPasswordValues: PasswordUpdateFormValues = {
    password: "",
    newPassword: "",
    confirmPassword: ""
  };

  async function handlePasswordSubmit(
    { password, newPassword }: PasswordUpdateFormValues,
    { setFieldError, setSubmitting }: FormikHelpers<PasswordUpdateFormValues>
  ) {
    setMadePasswordChanges(false);
    setSubmitting(true);
    const req = await fetch("/api/changePassword", {
      method: "POST",
      headers: {
        "Content-Type": "application/json"
      },
      body: JSON.stringify({
        currentPassword: password,
        newPassword: newPassword
      })
    });
    setSubmitting(false);

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

      if (!res.ok && res.error !== null) {
        switch (res.error) {
          case "incorrectPassword":
            setFieldError("password", "Incorrect password.");
            break;
        }
      }
      setMadePasswordChanges(true);
    } catch {
      console.error(req);
    }
  }

  return (
    <div className={styles.content}>
      <h2 className={styles.userName}>{info.username}</h2>
      <PrettyForm globalError={globalError}>
        <Formik
          initialValues={initialValues}
          onSubmit={handleFormSubmit}
          validationSchema={aboutMeSchema}
        >
          {({ isSubmitting }) => (
            <Form className={styles.profileGrid}>
              {madeProfileChanges ? <Toast>Saved your changes.</Toast> : null}
              <Input
                type="text"
                name="username"
                label="Username"
                defaultValue={info.username}
                disabled
                title="You can't change your username."
              />
              <Input
                type="text"
                name="displayName"
                label="Display name"
                defaultValue={info.displayName}
              />
              <Input
                type="email"
                name="email"
                label="Email"
                defaultValue={info.email}
              />

              <Input
                type="file"
                name="avatar"
                label="Avatar"
                accept="image/png, image/jpeg"
                customRender={(fieldProps) => (
                  <AvatarChanger
                    currentAvatarBlob={fieldProps.field.value}
                    onChange={(newBlob) =>
                      fieldProps.form.setFieldValue("avatar", newBlob)
                    }
                  />
                )}
              />

              <input
                type="submit"
                value="Save"
                className={styles.fancyInput}
                disabled={isSubmitting}
              />
            </Form>
          )}
        </Formik>
      </PrettyForm>
      <hr className={styles.divider} />
      <h2 className={styles.header}>Change password</h2>
      <PrettyForm globalError={passwordError}>
        <Formik
          initialValues={initialPasswordValues}
          onSubmit={handlePasswordSubmit}
          validationSchema={passwordUpdateSchema}
        >
          {({ isSubmitting }) => (
            <Form>
              {madePasswordChanges ? (
                <Toast>Changed your password.</Toast>
              ) : null}
              <Input
                type="password"
                name="password"
                label="Current"
                minLength={12}
                required
              />

              <Input
                type="password"
                name="newPassword"
                label="New"
                minLength={12}
                required
              />

              <Input
                type="password"
                name="confirmPassword"
                label="Confirm"
                minLength={12}
                required
              />

              <input
                type="submit"
                value="Save"
                className={styles.fancyInput}
                disabled={isSubmitting}
              />
            </Form>
          )}
        </Formik>
      </PrettyForm>

      <hr className={styles.divider} />
      <h2 className={styles.header}>Connections</h2>
      <div className={styles.authProviderList}>
        {providers.map((provider) => (
          <AuthProviderEntry provider={provider} key={provider.name} />
        ))}
      </div>

      <hr className={styles.divider} />
      <input
        type="button"
        value="Log out"
        className={styles.logout}
        onClick={async () => {
          document.cookie =
            "ticket=; expires=" + new Date().toUTCString() + "; path=/";
          window.location.href = "/";
        }}
      />
    </div>
  );
}
