import './style.scss';
import { useStore } from '@tanstack/react-form';
import { useMutation } from '@tanstack/react-query';
import type { AxiosError } from 'axios';
import clsx from 'clsx';
import { Fragment, useCallback, useEffect, useMemo, useState } from 'react';
import type z from 'zod';
import { m } from '../../../../paraglide/messages';
import api from '../../../api/api';
import type { ApiError, User } from '../../../api/types';
import { Icon } from '../../../defguard-ui/components/Icon';
import type { IconKindValue } from '../../../defguard-ui/components/Icon/icon-types';
import { Modal } from '../../../defguard-ui/components/Modal/Modal';
import { ModalControls } from '../../../defguard-ui/components/ModalControls/ModalControls';
import { isPresent } from '../../../defguard-ui/utils/isPresent';
import { useAppForm, withForm } from '../../../form';
import { formChangeLogic } from '../../../formLogic';
import {
  closeModal,
  subscribeCloseModal,
  subscribeOpenModal,
} from '../../../hooks/modalControls/modalsSubjects';
import type { ModalNameValue } from '../../../hooks/modalControls/modalTypes';
import {
  adminChangePasswordDefaultValues,
  adminChangePasswordSchema,
  mapPasswordFieldError,
  type PasswordErrorCodeValue,
  userChangePasswordSchema,
} from './form';

const modalNameKey: ModalNameValue = 'changePassword' as const;

export const ChangePasswordModal = () => {
  const [isOpen, setOpen] = useState(false);
  const [user, setUser] = useState<User | null>(null);
  const [isAdmin, setAdmin] = useState(false);

  useEffect(() => {
    const openSub = subscribeOpenModal(modalNameKey, (data) => {
      setUser(data.user);
      setAdmin(data.adminForm);
      setOpen(true);
    });
    const closeSub = subscribeCloseModal(modalNameKey, () => setOpen(false));
    return () => {
      openSub.unsubscribe();
      closeSub.unsubscribe();
    };
  }, []);

  return (
    <Modal
      id="change-password-modal"
      size="small"
      title={m.modal_change_password_title()}
      isOpen={isOpen}
      onClose={() => setOpen(false)}
      afterClose={() => {
        setAdmin(false);
        setUser(null);
      }}
    >
      {isPresent(user) && <ModalContent isAdmin={isAdmin} user={user} />}
    </Modal>
  );
};

const ModalContent = ({ isAdmin, user }: { isAdmin: boolean; user: User }) => {
  const formSchema = useMemo(() => {
    if (isAdmin) {
      return adminChangePasswordSchema;
    }
    return userChangePasswordSchema;
  }, [isAdmin]);

  const onSuccess = useCallback(() => {
    closeModal(modalNameKey);
  }, []);

  const { mutateAsync: mutateAdmin } = useMutation({
    mutationFn: api.user.adminChangePassword,
    onSuccess,
    meta: {
      invalidate: [['user', user.username]],
    },
  });

  const { mutateAsync: mutateUser } = useMutation({
    mutationFn: api.user.changePassword,
    onSuccess,
    onError: (e: AxiosError<ApiError>) => {
      const respCode = e.response?.status;
      if (isPresent(respCode) && respCode !== 200) {
        form.setErrorMap({
          onSubmit: {
            fields: {
              current: m.form_error_current_password(),
            },
          },
        });
      }
    },
    meta: {
      invalidate: [['me'], ['user', user.username], ['session-info']],
    },
  });

  const form = useAppForm({
    validationLogic: formChangeLogic,
    defaultValues: adminChangePasswordDefaultValues,
    onSubmit: async ({ value }) => {
      if (isAdmin) {
        await mutateAdmin({
          new_password: value.password,
          username: user.username,
        });
      } else {
        await mutateUser({
          new_password: value.password,
          old_password: value.current,
        });
      }
    },
    validators: {
      onChange: formSchema,
      onSubmit: formSchema,
    },
  });

  const isSubmitting = useStore(form.store, (s) => s.isSubmitting);

  // biome-ignore lint/correctness/useExhaustiveDependencies: side effect
  useEffect(() => {
    form.reset();
  }, [isAdmin]);

  return (
    <>
      <form
        onSubmit={(e) => {
          e.preventDefault();
          e.stopPropagation();
          form.handleSubmit();
        }}
      >
        <form.AppForm>
          {!isAdmin && (
            <Fragment>
              <form.AppField name="current">
                {(field) => (
                  <field.FormInput
                    required
                    type="password"
                    label={m.form_label_current_password()}
                    helper={m.form_helper_current_password()}
                  />
                )}
              </form.AppField>
              <form.AppField name="password">
                {(field) => (
                  <field.FormInput
                    required
                    type="password"
                    label={m.form_label_new_password()}
                    mapError={mapPasswordFieldError}
                    helper={m.form_helper_new_password()}
                  />
                )}
              </form.AppField>
              <form.AppField name="repeat">
                {(field) => (
                  <field.FormInput
                    required
                    type="password"
                    label={m.form_label_confirm_new_password()}
                    helper={m.form_helper_confirm_new_password()}
                  />
                )}
              </form.AppField>
            </Fragment>
          )}
          {isAdmin && (
            <form.AppField name="password">
              {(field) => (
                <field.FormInput
                  required
                  type="password"
                  label={m.form_label_new_password()}
                  mapError={mapPasswordFieldError}
                  helper={m.form_helper_new_password()}
                />
              )}
            </form.AppField>
          )}
          <CheckList form={form} />
        </form.AppForm>
      </form>
      <ModalControls
        cancelProps={{
          text: m.controls_cancel(),
          disabled: isSubmitting,
          onClick: () => {
            closeModal(modalNameKey);
          },
        }}
        submitProps={{
          text: m.modal_change_password_submit(),
          loading: isSubmitting,
          testId: 'submit-password-change',
          onClick: () => {
            form.handleSubmit();
          },
        }}
      />
    </>
  );
};

const CheckList = withForm({
  defaultValues: adminChangePasswordDefaultValues,
  render: ({ form }) => {
    const passwordFieldErrors = useStore(
      form.store,
      (state) =>
        (state.fieldMeta.password?.errors as z.core.$ZodIssue[])
          ?.filter((issue) => issue.code === 'custom')
          .map((issue) => issue.message) ?? [],
    );

    const isPasswordClean = useStore(
      form.store,
      (state) => state.fieldMeta.password?.isPristine ?? true,
    );

    const isChecked = useCallback(
      (value: PasswordErrorCodeValue): boolean =>
        !passwordFieldErrors.includes(value) && !isPasswordClean,
      [passwordFieldErrors, isPasswordClean],
    );

    return (
      <div className="checklist">
        <p>{m.password_form_check_title()}</p>
        <ul>
          <CheckListItem
            errorCode="password_form_check_minimum"
            checked={isChecked('password_form_check_minimum')}
          />
          <CheckListItem
            errorCode="password_form_check_number"
            checked={isChecked('password_form_check_number')}
          />
          <CheckListItem
            errorCode="password_form_check_special"
            checked={isChecked('password_form_check_special')}
          />
          <CheckListItem
            errorCode="password_form_check_lowercase"
            checked={isChecked('password_form_check_lowercase')}
          />
          <CheckListItem
            errorCode="password_form_check_uppercase"
            checked={isChecked('password_form_check_uppercase')}
          />
        </ul>
      </div>
    );
  },
});

const CheckListItem = ({
  checked,
  errorCode,
}: {
  errorCode: PasswordErrorCodeValue;
  checked: boolean;
}) => {
  const iconKind: IconKindValue = checked ? 'check-filled' : 'empty-point';

  return (
    <li
      className={clsx({
        active: checked,
      })}
    >
      <Icon icon={iconKind} size={16} /> <span>{m[errorCode]()}</span>
    </li>
  );
};
