/**
 * Copyright (c) 2023-present Pi Dash Software, Inc. and contributors
 * SPDX-License-Identifier: AGPL-3.0-only
 * See the LICENSE file for details.
 */

import { useState } from "react";
import { observer } from "mobx-react";
import { Controller, useForm } from "react-hook-form";
import { Eye, EyeOff } from "lucide-react";
// pi dash imports
import { E_PASSWORD_STRENGTH } from "@pi-dash/constants";
import { useTranslation } from "@pi-dash/i18n";
import { Button } from "@pi-dash/propel/button";
import { TOAST_TYPE, setToast } from "@pi-dash/propel/toast";
import { Input, PasswordStrengthIndicator } from "@pi-dash/ui";
import { getPasswordStrength } from "@pi-dash/utils";
// components
import { ProfileSettingsHeading } from "@/components/settings/profile/heading";
// helpers
import { authErrorHandler, EAuthenticationErrorCodes, passwordErrors } from "@/helpers/authentication.helper";
// hooks
import { useUser } from "@/hooks/store/user";
// services
import { AuthService } from "@/services/auth.service";

export interface FormValues {
  old_password: string;
  new_password: string;
  confirm_password: string;
}

const defaultValues: FormValues = {
  old_password: "",
  new_password: "",
  confirm_password: "",
};

const authService = new AuthService();

const defaultShowPassword = {
  oldPassword: false,
  password: false,
  confirmPassword: false,
};

export const SecurityProfileSettings = observer(function SecurityProfileSettings() {
  // store
  const { data: currentUser, changePassword } = useUser();
  // states
  const [showPassword, setShowPassword] = useState(defaultShowPassword);
  const [isPasswordInputFocused, setIsPasswordInputFocused] = useState(false);
  const [isRetryPasswordInputFocused, setIsRetryPasswordInputFocused] = useState(false);

  // use form
  const {
    control,
    handleSubmit,
    watch,
    setError,
    formState: { errors, isSubmitting },
    reset,
  } = useForm<FormValues>({ defaultValues });
  // derived values
  const oldPassword = watch("old_password");
  const password = watch("new_password");
  const confirmPassword = watch("confirm_password");
  const oldPasswordRequired = !currentUser?.is_password_autoset;
  // i18n
  const { t } = useTranslation();

  const isNewPasswordSameAsOldPassword = oldPassword !== "" && password !== "" && password === oldPassword;

  const handleShowPassword = (key: keyof typeof showPassword) =>
    setShowPassword((prev) => ({ ...prev, [key]: !prev[key] }));

  const handleChangePassword = async (formData: FormValues) => {
    const { old_password, new_password } = formData;
    try {
      const csrfToken = await authService.requestCSRFToken().then((data) => data?.csrf_token);
      if (!csrfToken) throw new Error("csrf token not found");

      await changePassword(csrfToken, {
        ...(oldPasswordRequired && { old_password }),
        new_password,
      });

      reset(defaultValues);
      setShowPassword(defaultShowPassword);
      setToast({
        type: TOAST_TYPE.SUCCESS,
        title: t("Success!"),
        message: t("Password changed successfully."),
      });
    } catch (error: unknown) {
      const err = error as Error & { error_code?: string };
      const code = err.error_code?.toString();
      const errorInfo = code ? authErrorHandler(code as EAuthenticationErrorCodes) : undefined;

      setToast({
        type: TOAST_TYPE.ERROR,
        title: errorInfo?.title ?? t("Authentication error"),
        message:
          typeof errorInfo?.message === "string" ? errorInfo.message : t("Something went wrong. Please try again."),
      });

      if (code && passwordErrors.includes(code as EAuthenticationErrorCodes)) {
        setError("new_password", {
          type: "manual",
          message: errorInfo?.message?.toString() || t("Something went wrong. Please try again."),
        });
      }
    }
  };

  const isButtonDisabled =
    getPasswordStrength(password) != E_PASSWORD_STRENGTH.STRENGTH_VALID ||
    (oldPasswordRequired && oldPassword.trim() === "") ||
    password.trim() === "" ||
    confirmPassword.trim() === "" ||
    password !== confirmPassword ||
    password === oldPassword;

  const passwordSupport = password.length > 0 &&
    getPasswordStrength(password) != E_PASSWORD_STRENGTH.STRENGTH_VALID && (
      <PasswordStrengthIndicator password={password} isFocused={isPasswordInputFocused} />
    );

  const renderPasswordMatchError = !isRetryPasswordInputFocused || confirmPassword.length >= password.length;

  return (
    <div className="size-full">
      <ProfileSettingsHeading title={t("Change password")} />
      <form onSubmit={handleSubmit(handleChangePassword)} className="mt-7 flex flex-col gap-8">
        <div className="flex flex-col gap-y-7">
          {oldPasswordRequired && (
            <div className="flex flex-col gap-y-2">
              <h4 className="text-13">{t("Current password")}</h4>
              <div className="relative flex items-center rounded-md">
                <Controller
                  control={control}
                  name="old_password"
                  rules={{
                    required: t("This field is required"),
                  }}
                  render={({ field: { value, onChange } }) => (
                    <Input
                      id="old_password"
                      type={showPassword?.oldPassword ? "text" : "password"}
                      value={value}
                      onChange={onChange}
                      placeholder={t("Old password")}
                      className="w-full"
                      hasError={Boolean(errors.old_password)}
                      autoComplete="current-password"
                    />
                  )}
                />
                {showPassword?.oldPassword ? (
                  <EyeOff
                    className="absolute right-3 h-5 w-5 stroke-placeholder hover:cursor-pointer"
                    onClick={() => handleShowPassword("oldPassword")}
                  />
                ) : (
                  <Eye
                    className="absolute right-3 h-5 w-5 stroke-placeholder hover:cursor-pointer"
                    onClick={() => handleShowPassword("oldPassword")}
                  />
                )}
              </div>
              {errors.old_password && (
                <span className="text-11 text-danger-primary">{errors.old_password.message}</span>
              )}
            </div>
          )}
          <div className="grid gap-x-4 gap-y-7 sm:grid-cols-2">
            <div className="flex flex-col gap-y-2">
              <h4 className="text-13">{t("New password")}</h4>
              <div className="relative flex items-center rounded-md">
                <Controller
                  control={control}
                  name="new_password"
                  rules={{
                    required: t("This field is required"),
                  }}
                  render={({ field: { value, onChange } }) => (
                    <Input
                      id="new_password"
                      type={showPassword?.password ? "text" : "password"}
                      value={value}
                      placeholder={t("Enter new password")}
                      onChange={onChange}
                      className="w-full"
                      hasError={Boolean(errors.new_password)}
                      onFocus={() => setIsPasswordInputFocused(true)}
                      onBlur={() => setIsPasswordInputFocused(false)}
                      autoComplete="new-password"
                    />
                  )}
                />
                {showPassword?.password ? (
                  <EyeOff
                    className="absolute right-3 h-5 w-5 stroke-placeholder hover:cursor-pointer"
                    onClick={() => handleShowPassword("password")}
                  />
                ) : (
                  <Eye
                    className="absolute right-3 h-5 w-5 stroke-placeholder hover:cursor-pointer"
                    onClick={() => handleShowPassword("password")}
                  />
                )}
              </div>
              {passwordSupport}
              {errors.new_password && (
                <span className="text-11 text-danger-primary">{errors.new_password.message}</span>
              )}
              {isNewPasswordSameAsOldPassword && !isPasswordInputFocused && (
                <span className="text-11 text-danger-primary">
                  {t("New password must be different from old password")}
                </span>
              )}
            </div>
            <div className="flex flex-col gap-y-2">
              <h4 className="text-13">{t("Confirm password")}</h4>
              <div className="relative flex items-center rounded-md">
                <Controller
                  control={control}
                  name="confirm_password"
                  rules={{
                    required: t("This field is required"),
                  }}
                  render={({ field: { value, onChange } }) => (
                    <Input
                      id="confirm_password"
                      type={showPassword?.confirmPassword ? "text" : "password"}
                      placeholder={t("Confirm password")}
                      value={value}
                      onChange={onChange}
                      className="w-full"
                      hasError={Boolean(errors.confirm_password)}
                      onFocus={() => setIsRetryPasswordInputFocused(true)}
                      onBlur={() => setIsRetryPasswordInputFocused(false)}
                      autoComplete="new-password"
                    />
                  )}
                />
                {showPassword?.confirmPassword ? (
                  <EyeOff
                    className="absolute right-3 h-5 w-5 stroke-placeholder hover:cursor-pointer"
                    onClick={() => handleShowPassword("confirmPassword")}
                  />
                ) : (
                  <Eye
                    className="absolute right-3 h-5 w-5 stroke-placeholder hover:cursor-pointer"
                    onClick={() => handleShowPassword("confirmPassword")}
                  />
                )}
              </div>
              {!!confirmPassword && password !== confirmPassword && renderPasswordMatchError && (
                <span className="text-13 text-danger-primary">{t("Passwords don't match")}</span>
              )}
            </div>
          </div>
          <div>
            <Button variant="primary" size="xl" type="submit" loading={isSubmitting} disabled={isButtonDisabled}>
              {isSubmitting
                ? `${t("Changing password")}`
                : t("Change password")}
            </Button>
          </div>
        </div>
      </form>
    </div>
  );
});
