import { AuthLayout } from "../../../AuthLayout";
import { Form } from "../../../Form";
import * as inputs from "../../../inputs";
import gql from "gql-tag";
import { useMutation } from "urql";
import { Alert } from "../../../Alert";
import { useCallback, useContext, useEffect, useMemo } from "react";
import { useLocation, useNavigate } from "react-router-dom";
import { Context as AuthContext } from "../../../../contexts/AuthContext";
import { useFormik } from "formik";
import * as Yup from "yup";

import { AnchorLinkElement } from "../_molecules/_atoms/AnchorLinkElement";
import { EmailInputElement } from "../_molecules/_atoms/EmailInputElement";
import { PasswordInputElement } from "../_molecules/_atoms/PasswordInputElement";
import { SubmitButtonElement } from "../_molecules/_atoms/SubmitButtonElement";
import {
  SIGNUP_COMPONENT,
  SIGNUP_EMAIL,
  SIGNUP_PASSWORD,
  SIGNUP_BUTTON,
  SIGNUP_ERROR,
  SIGNUP_TERMS,
  SIGNUP_PASSWORD_CONFIRM,
  SIGNUP_SUCCESS,
} from "../../../../testing";

const requestMutation = gql`
  mutation RequestMutation($email: String!, $password: String!) {
    userRegistrationRequest(input: { email: $email, password: $password }) {
      success
      message
    }
  }
`;

const callbackMutation = gql`
  mutation CallbackMutation($registrationToken: String!) {
    userRegistrationCallback(input: { registrationToken: $registrationToken }) {
      success
      message
      accessToken
      refreshToken
    }
  }
`;

function useURLSearchParams() {
  const { search } = useLocation();

  return useMemo(() => new URLSearchParams(search), [search]);
}

export function Signup() {
  const navigate = useNavigate();
  const [signupRequestData, signUpRequest] = useMutation(requestMutation);
  const [signupCallbackData, signupCallback] = useMutation(callbackMutation);
  const params = useURLSearchParams();
  const disabled = Boolean(
    signupRequestData.fetching ||
      signupRequestData?.data?.userRegistrationRequest?.success,
  );
  const auth = useContext(AuthContext);

  const validation = useFormik({
    enableReinitialize: true,
    initialValues: {
      email: "",
      password: "",
      confirmPassword: "",
      acceptTerms: false,
    },

    validationSchema: Yup.object().shape({
      email: Yup.string()
        .email("You must enter a valid email address.")
        .required("You must enter an email address."),
      password: Yup.string()
        .min(8, "Password is too short - must be at least 8 chars.")
        .required("Please enter your password."),
      confirmPassword: Yup.string()
        .required("Confirm Password is required")
        .oneOf([Yup.ref("password")], "Confirm Password does not match"),
      acceptTerms: Yup.boolean().oneOf([true], "Please Accept"),
    }),
    onSubmit: ({ email, password }, actions) => {
      signUpRequest({
        email: email.trim(),
        password: password.trim(),
      });
      actions.resetForm();
    },
  });

  const onCallback = useCallback(() => {
    signupCallback({
      registrationToken: params.get("token"),
    });
  }, [params]);

  useEffect(() => {
    if (params.get("token")) {
      onCallback();
    }
  }, [params.get("token")]);

  useEffect(() => {
    const isSuccess = signupCallbackData.data?.userRegistrationCallback.success;
    if (!isSuccess) {
      return;
    }

    const accessToken = signupCallbackData.data?.userRegistrationCallback
      .accessToken as string;

    const refreshToken = signupCallbackData.data?.userRegistrationCallback
      .refreshToken as string;

    auth.setValue({ accessToken, refreshToken });

    navigate("/dashboard");
  }, [signupCallbackData, auth.setValue]);

  if (params.get("token")) {
    const isUserRegistrationCallbackError =
      signupCallbackData.error ||
      signupCallbackData.data?.userRegistrationCallback?.success === false;

    const userRegistrationCallbackError =
      signupCallbackData.error?.message ||
      signupCallbackData.data?.userRegistrationCallback?.message;

    return (
      <AuthLayout type="EmailVerification">
        {isUserRegistrationCallbackError ? (
          <Alert color="red" message={userRegistrationCallbackError} />
        ) : (
          <div>
            {signupCallbackData.fetching && (
              <Alert color="blue" message="Verifying your email address..." />
            )}
          </div>
        )}
      </AuthLayout>
    );
  }

  const successMessage = "Click the link sent to your email";

  const errorMessage =
    signupRequestData.error?.message ||
    signupRequestData.data?.userRegistrationRequest?.message;

  return (
    <AuthLayout type="Signup">
      <Form
        id={SIGNUP_COMPONENT}
        onSubmit={(e: React.FormEvent) => {
          e.preventDefault();
          validation.handleSubmit();
          return false;
        }}
      >
        {(signupRequestData.error ||
          signupRequestData.data?.userRegistrationRequest?.success ===
            false) && (
          <div id={SIGNUP_ERROR} data-error={errorMessage}>
            <Alert color="red" message={errorMessage} />
          </div>
        )}
        <div className="flex mt-2 flex-row items-center text-sm mb-4">
          <span>Already have an account?&nbsp;</span>
          <AnchorLinkElement toLink="/login" text="Sign in" />
        </div>
        {signupRequestData?.data?.userRegistrationRequest?.success && (
          <div
            id={SIGNUP_SUCCESS}
            data-message={successMessage}
            className="m-3"
          >
            <Alert color="green" message={successMessage} />
          </div>
        )}
        <EmailInputElement
          name="email"
          id={SIGNUP_EMAIL}
          disabled={disabled}
          required
          error={validation.errors.email ? true : false}
          onChange={validation.handleChange}
          onBlur={validation.handleBlur}
          helperText={validation.errors.email}
        />
        <PasswordInputElement
          id={SIGNUP_PASSWORD}
          name="password"
          disabled={disabled}
          error={validation.errors.password ? true : false}
          onChange={validation.handleChange}
          onBlur={validation.handleBlur}
          helperText={validation.errors.password}
        />
        <PasswordInputElement
          id={SIGNUP_PASSWORD_CONFIRM}
          name="confirmPassword"
          disabled={disabled}
          error={validation.errors.confirmPassword ? true : false}
          onChange={validation.handleChange}
          onBlur={validation.handleBlur}
          helperText={validation.errors.confirmPassword}
          isConfirm
        />
        <div className="flex flex-col mb-2 mt-2">
          <inputs.CheckBox
            id={SIGNUP_TERMS}
            name="acceptTerms"
            value={validation.values.acceptTerms}
            label="I agree to the Terms of Service and Privacy Policy"
            onChange={(e) =>
              validation.setFieldValue("acceptTerms", e.target.checked)
            }
          />
          {validation.errors.acceptTerms ? (
            <span className="text-error-red text-sm ml-5">
              {validation.errors.acceptTerms}
            </span>
          ) : null}
        </div>
        <SubmitButtonElement
          id={SIGNUP_BUTTON}
          content={"Sign up"}
          disabled={disabled}
          type="submit"
        />
      </Form>
    </AuthLayout>
  );
}
