import { Localized } from "@fluent/react/compat";
import cn from "classnames";
import { FORM_ERROR, FormApi } from "final-form";
import React, {
  FunctionComponent,
  useCallback,
  useMemo,
  useState,
} from "react";
import { Field, Form } from "react-final-form";

import { ALLOWED_USERNAME_CHANGE_TIMEFRAME_DURATION } from "coral-common/constants";
import { reduceSeconds } from "coral-common/helpers/i18n";
import TIME from "coral-common/time";
import getAuthenticationIntegrations from "coral-framework/helpers/getAuthenticationIntegrations";
import { useCoralContext } from "coral-framework/lib/bootstrap";
import { InvalidRequestError } from "coral-framework/lib/errors";
import { useViewerEvent } from "coral-framework/lib/events";
import {
  graphql,
  useMutation,
  withFragmentContainer,
} from "coral-framework/lib/relay";
import {
  composeValidators,
  required,
  validateUsername,
  validateUsernameEquals,
} from "coral-framework/lib/validation";
import CLASSES from "coral-stream/classes";
import FieldValidationMessage from "coral-stream/common/FieldValidationMessage";
import { ShowEditUsernameDialogEvent } from "coral-stream/events";
import {
  Box,
  Button,
  CallOut,
  CardCloseButton,
  Flex,
  FormField,
  HorizontalGutter,
  Icon,
  InputLabel,
  TextField,
  Typography,
} from "coral-ui/components";

import { ChangeUsernameContainer_settings as SettingsData } from "coral-stream/__generated__/ChangeUsernameContainer_settings.graphql";
import { ChangeUsernameContainer_viewer as ViewerData } from "coral-stream/__generated__/ChangeUsernameContainer_viewer.graphql";

import UpdateUsernameMutation from "./UpdateUsernameMutation";

import styles from "./ChangeUsernameContainer.css";

const FREQUENCYSCALED = reduceSeconds(
  ALLOWED_USERNAME_CHANGE_TIMEFRAME_DURATION,
  [TIME.DAY]
);

interface Props {
  viewer: ViewerData;
  settings: SettingsData;
}

interface FormProps {
  username: string;
  usernameConfirm: string;
}

const ChangeUsernameContainer: FunctionComponent<Props> = ({
  viewer,
  settings,
}) => {
  const emitShowEditUsernameDialog = useViewerEvent(
    ShowEditUsernameDialogEvent
  );
  const [showEditForm, setShowEditForm] = useState(false);
  const [showSuccessMessage, setShowSuccessMessage] = useState(false);
  const toggleEditForm = useCallback(() => {
    if (!showEditForm) {
      emitShowEditUsernameDialog();
    }
    setShowEditForm(!showEditForm);
  }, [setShowEditForm, showEditForm]);
  const updateUsername = useMutation(UpdateUsernameMutation);

  const closeSuccessMessage = useCallback(() => setShowSuccessMessage(false), [
    setShowEditForm,
  ]);

  const canChangeLocalAuth = useMemo(() => {
    if (!settings.accountFeatures.changeUsername) {
      return false;
    }
    if (
      !viewer.profiles.find(profile => profile.__typename === "LocalProfile")
    ) {
      return false;
    }
    const enabled = getAuthenticationIntegrations(settings.auth, "stream");

    return (
      enabled.includes("local") ||
      !(enabled.length === 1 && enabled[0] === "sso")
    );
  }, [viewer, settings]);

  const canChangeUsername = useMemo(() => {
    const { username } = viewer.status;
    if (username && username.history.length > 1) {
      const lastUsernameEditAllowed = new Date();
      lastUsernameEditAllowed.setSeconds(
        lastUsernameEditAllowed.getSeconds() -
          ALLOWED_USERNAME_CHANGE_TIMEFRAME_DURATION
      );
      const lastUsernameEdit = new Date(
        username.history[username.history.length - 1].createdAt
      );
      return lastUsernameEdit <= lastUsernameEditAllowed;
    }
    return true;
  }, [viewer]);

  const canChangeUsernameDate = useMemo(() => {
    const { username } = viewer.status;
    if (username && username.history.length > 1) {
      const date = new Date(
        username.history[username.history.length - 1].createdAt
      );
      date.setSeconds(
        date.getSeconds() + ALLOWED_USERNAME_CHANGE_TIMEFRAME_DURATION
      );
      return date;
    }
    return null;
  }, [viewer]);

  const onSubmit = useCallback(
    async (input: FormProps, form: FormApi) => {
      try {
        await updateUsername({
          username: input.username,
        });
      } catch (err) {
        if (err instanceof InvalidRequestError) {
          return err.invalidArgs;
        }

        return {
          [FORM_ERROR]: err.message,
        };
      }

      setShowEditForm(false);
      setShowSuccessMessage(true);

      return;
    },
    [updateUsername]
  );

  const { locales } = useCoralContext();

  const formatter = new Intl.DateTimeFormat(locales, {
    day: "2-digit",
    month: "2-digit",
    year: "numeric",
  });

  return (
    <HorizontalGutter spacing={5} data-testid="profile-changeUsername">
      {showSuccessMessage && (
        <Box className={styles.successMessage}>
          <Flex justifyContent="space-between" alignItems="center">
            <Localized id="profile-changeUsername-success">
              <Typography>
                Your username has been successfully updated
              </Typography>
            </Localized>
            <CardCloseButton
              className={styles.closeButton}
              onClick={closeSuccessMessage}
            />
          </Flex>
        </Box>
      )}
      {!showEditForm && (
        <Flex alignItems="baseline" justifyContent="space-between">
          <div>
            <Localized id="profile-changeUsername-username">
              <Typography
                className={CLASSES.myUsername.username}
                color="textDark"
                variant="heading2"
              >
                Username
              </Typography>
            </Localized>
            <Typography variant="bodyCopy">{viewer.username}</Typography>
          </div>
          {canChangeLocalAuth && (
            <Localized id="profile-changeUsername-edit">
              <Button
                className={CLASSES.myUsername.editButton}
                variant="outlineFilled"
                size="small"
                color="primary"
                onClick={toggleEditForm}
              >
                Edit
              </Button>
            </Localized>
          )}
        </Flex>
      )}
      {showEditForm && (
        <CallOut
          borderless
          className={cn(styles.callOut, CLASSES.myUsername.form.$root)}
          color="primary"
        >
          <HorizontalGutter spacing={4}>
            <div>
              <Localized id="profile-changeUsername-heading">
                <Typography variant="heading1" gutterBottom color="textDark">
                  Edit your username
                </Typography>
              </Localized>
              <Localized
                id="profile-changeUsername-desc-text"
                $value={FREQUENCYSCALED.scaled}
                $unit={FREQUENCYSCALED.unit}
              >
                <Typography color="textDark">
                  Change the username that will appear on all of your past and
                  future comments. Usernames can be changed once every{" "}
                  {FREQUENCYSCALED.scaled} {FREQUENCYSCALED.unit}.
                </Typography>
              </Localized>
            </div>
            <div>
              <Localized id="profile-changeUsername-current">
                <Typography
                  variant="bodyCopyBold"
                  color="textPrimary"
                  className={CLASSES.myUsername.form.username}
                >
                  Current username
                </Typography>
              </Localized>
              <Typography variant="heading2" color="textDark">
                {viewer.username}
              </Typography>
            </div>
            {canChangeUsername && (
              <Form onSubmit={onSubmit}>
                {({ handleSubmit, submitError, pristine, invalid }) => (
                  <form
                    onSubmit={handleSubmit}
                    data-testid="profile-changeUsername-form"
                  >
                    <HorizontalGutter spacing={4}>
                      <FormField>
                        <HorizontalGutter>
                          <Localized id="profile-changeUsername-newUsername-label">
                            <InputLabel htmlFor="profile-changeUsername-username">
                              New username
                            </InputLabel>
                          </Localized>
                          <Field
                            name="username"
                            validate={composeValidators(
                              required,
                              validateUsername
                            )}
                            id="profile-changeUsername-username"
                          >
                            {({ input, meta }) => (
                              <>
                                <TextField
                                  {...input}
                                  fullWidth
                                  id="profile-changeUsername-username"
                                />
                                <FieldValidationMessage meta={meta} />
                              </>
                            )}
                          </Field>
                        </HorizontalGutter>
                      </FormField>
                      <FormField>
                        <HorizontalGutter>
                          <Localized id="profile-changeUsername-confirmNewUsername-label">
                            <InputLabel htmlFor="profile-changeUsername-username-confirm">
                              Confirm new username
                            </InputLabel>
                          </Localized>
                          <Field
                            name="usernameConfirm"
                            validate={composeValidators(
                              required,
                              validateUsernameEquals
                            )}
                          >
                            {({ input, meta }) => (
                              <>
                                <TextField
                                  {...input}
                                  fullWidth
                                  id="profile-changeUsername-username-confirm"
                                />
                                <FieldValidationMessage meta={meta} />
                              </>
                            )}
                          </Field>
                        </HorizontalGutter>
                      </FormField>
                      {submitError && (
                        <CallOut
                          className={CLASSES.myUsername.form.errorMessage}
                          color="error"
                          fullWidth
                        >
                          {submitError}
                        </CallOut>
                      )}
                    </HorizontalGutter>
                    <Flex justifyContent="flex-end" className={styles.footer}>
                      <Localized id="profile-changeUsername-cancel">
                        <Button
                          className={CLASSES.myUsername.form.cancelButton}
                          type="button"
                          onClick={toggleEditForm}
                        >
                          Cancel
                        </Button>
                      </Localized>
                      <Localized id="profile-changeUsername-save">
                        <Button
                          className={CLASSES.myUsername.form.saveButton}
                          variant={pristine || invalid ? "outlined" : "filled"}
                          type="submit"
                          data-testid="profile-changeUsername-save"
                          color={pristine || invalid ? "regular" : "primary"}
                          disabled={pristine || invalid}
                        >
                          <span>Save</span>
                        </Button>
                      </Localized>
                    </Flex>
                  </form>
                )}
              </Form>
            )}
            {!canChangeUsername && (
              <div data-testid="profile-changeUsername-cantChange">
                <Flex>
                  <Icon size="md" className={styles.errorIcon}>
                    error
                  </Icon>
                  <Localized
                    date={canChangeUsernameDate}
                    id="profile-changeUsername-recentChange"
                    $value={FREQUENCYSCALED.scaled}
                    $unit={FREQUENCYSCALED.unit}
                    $nextUpdate={
                      canChangeUsernameDate
                        ? formatter.format(canChangeUsernameDate)
                        : null
                    }
                  >
                    <Typography className={styles.tooSoon}>
                      Your username has been changed in the last{" "}
                      {FREQUENCYSCALED.scaled} {FREQUENCYSCALED.unit}. You may
                      change your username again on{" "}
                      {canChangeUsernameDate
                        ? formatter.format(canChangeUsernameDate)
                        : null}
                    </Typography>
                  </Localized>
                </Flex>
                <Flex justifyContent="flex-end">
                  <Localized id="profile-changeUsername-close">
                    <Button
                      color="primary"
                      variant="filled"
                      type="button"
                      onClick={toggleEditForm}
                      className={CLASSES.myUsername.form.closeButton}
                    >
                      Close
                    </Button>
                  </Localized>
                </Flex>
              </div>
            )}
          </HorizontalGutter>
        </CallOut>
      )}
    </HorizontalGutter>
  );
};

const enhanced = withFragmentContainer<Props>({
  viewer: graphql`
    fragment ChangeUsernameContainer_viewer on User {
      username
      profiles {
        __typename
      }
      status {
        username {
          history {
            username
            createdAt
          }
        }
      }
    }
  `,
  settings: graphql`
    fragment ChangeUsernameContainer_settings on Settings {
      accountFeatures {
        changeUsername
      }
      auth {
        integrations {
          local {
            enabled
            targetFilter {
              stream
            }
          }
          google {
            enabled
            targetFilter {
              stream
            }
          }
          oidc {
            enabled
            targetFilter {
              stream
            }
          }
          sso {
            enabled
            targetFilter {
              stream
            }
          }
          facebook {
            enabled
            targetFilter {
              stream
            }
          }
        }
      }
    }
  `,
})(ChangeUsernameContainer);

export default enhanced;
