import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Id, MediaPlanRelease } from 'backend-api/models';
import { bem } from 'utils/bem';
import { debounce } from 'lodash';
import { useProjectReleaseTypes } from 'hooks';
import { LoadingState } from 'common/types';
import { Loader } from './components';
import { getMediaPlanRelease, updateMediaPlanRelease } from 'media-plan-v2/actions';
import { projectReleaseSelector } from 'media-plan-v2/selectors';
import {
  INITIAL_RELEASE_NAME,
  INITIAL_RELEASE_TYPE,
  NONE_RELEASE_TYPE_ID,
  TYPES_DROPDOWN_IDENTIFIER,
} from './constants';
import { BEM_CLASS, SReleaseDetailsPopup } from './s-release-details-popup';
import {
  THEME,
  useManageToasts,
  Typography,
  TYPOGRAPHY_TYPE,
  Button,
  BUTTON_TYPE,
  BUTTON_SIZE,
  Input,
  INPUT_VARIANT,
  Select,
  SELECT_TYPE,
} from 'gdb-web-shared-components';
import { SelectOption } from './types';

interface ReleaseDetailsPopupProps {
  mediaPlanId: Id;
  projectId: Id;
  isOpened: boolean;
  isDisabled: boolean;
  controlRef: React.RefObject<HTMLButtonElement>;
  onClose(): void;
}

const classes = bem(BEM_CLASS);

export const ReleaseDetailsPopup = React.memo(
  ({ mediaPlanId, projectId, isOpened, isDisabled, controlRef, onClose }: ReleaseDetailsPopupProps) => {
    const rootRef = useRef<HTMLDivElement>(null);

    const dispatch = useDispatch();
    const projectReleaseTypes = useProjectReleaseTypes();
    const { openToast } = useManageToasts(THEME.light);

    const [position, setPosition] = useState(0);
    const [releaseName, setReleaseName] = useState<string>(INITIAL_RELEASE_NAME);
    const [releaseType, setReleaseType] = useState<number | null>(INITIAL_RELEASE_TYPE);

    const {
      loading,
      data: { name, type },
    } = useSelector(projectReleaseSelector);

    const options = useMemo<SelectOption[]>(() => {
      if (projectReleaseTypes) {
        return [
          { id: NONE_RELEASE_TYPE_ID, label: 'None' },
          ...projectReleaseTypes?.map(({ id, name }) => ({ id, label: name })),
        ];
      }

      return [];
    }, [projectReleaseTypes]);

    const debouncedReleaseUpdate = useMemo(
      () =>
        debounce(({ name, type }: MediaPlanRelease) => {
          dispatch(
            updateMediaPlanRelease.request({
              projectId,
              mediaPlanId,
              payload: { name, type },
              openToast,
              onError: onClose,
            })
          );
        }, 700),
      [dispatch, mediaPlanId, onClose, openToast, projectId]
    );

    const handleReleaseNameChange = useCallback(
      (event: React.ChangeEvent<HTMLInputElement>) => {
        const name = event.target.value ?? INITIAL_RELEASE_NAME;

        setReleaseName(name);
        debouncedReleaseUpdate({ name, type: undefined });
      },
      [debouncedReleaseUpdate]
    );

    const handleClearReleaseType = useCallback(() => {
      setReleaseType(INITIAL_RELEASE_TYPE);
      debouncedReleaseUpdate({ name: undefined, type: INITIAL_RELEASE_TYPE });
    }, [debouncedReleaseUpdate]);

    const handleReleaseTypeChange = useCallback(
      (value: number) => {
        if (value === NONE_RELEASE_TYPE_ID) {
          handleClearReleaseType();
          return;
        }

        setReleaseType(value);
        debouncedReleaseUpdate({ name: undefined, type: value });
      },
      [debouncedReleaseUpdate, handleClearReleaseType]
    );

    const handleRefreshClick = useCallback(
      (e: React.MouseEvent<HTMLButtonElement>) => {
        e.stopPropagation();

        dispatch(getMediaPlanRelease.request({ mediaPlanId, projectId }));
      },
      [dispatch, mediaPlanId, projectId]
    );

    useLayoutEffect(() => {
      const controlElement = controlRef?.current;

      if (controlElement && isOpened) {
        const rect = controlElement.getBoundingClientRect();

        setPosition(rect.height);
      }
    }, [controlRef, isOpened]);

    useEffect(() => {
      const handleClickAway = (event: MouseEvent) => {
        const selectDropdownClicked = event.composedPath().some(target => {
          if (target instanceof Element) {
            return target.classList.contains(TYPES_DROPDOWN_IDENTIFIER);
          }

          return false;
        });

        const shouldClosePopup =
          !selectDropdownClicked && event.target instanceof Node && !rootRef.current?.contains(event.target);

        if (shouldClosePopup) {
          onClose();
        }
      };

      document.addEventListener('mousedown', handleClickAway);

      return () => {
        document.removeEventListener('mousedown', handleClickAway);
      };
    }, [onClose]);

    useEffect(() => {
      if (isOpened && loading === LoadingState.Finished) {
        setReleaseName(name ?? INITIAL_RELEASE_NAME);
        setReleaseType(type ?? INITIAL_RELEASE_TYPE);
      }
    }, [isOpened, loading, name, type]);

    useEffect(() => {
      if (!mediaPlanId) return;
      dispatch(getMediaPlanRelease.request({ mediaPlanId, projectId }));
    }, [dispatch, mediaPlanId, projectId]);

    const renderContent = useMemo(() => {
      switch (loading) {
        case LoadingState.Idle:
        case LoadingState.Started:
          return <Loader />;
        case LoadingState.Failed:
          return (
            <div className={classes('error')}>
              <Typography type={TYPOGRAPHY_TYPE.body4} className={classes('error-text')}>
                An error occurred while loading Release
                <br />
                Details data, please try again.
              </Typography>
              <button className={classes('error-refresh')} onClick={handleRefreshClick}>
                <Typography type={TYPOGRAPHY_TYPE.body2}>Refresh</Typography>
              </button>
            </div>
          );

        case LoadingState.Finished:
          return (
            <>
              <div className={classes('content-item')}>
                <Typography type={TYPOGRAPHY_TYPE.body3} className={classes('content-title')}>
                  Release Name
                </Typography>
                <Input
                  inputClassName={classes('release-name-input')}
                  variant={INPUT_VARIANT.compact}
                  value={releaseName}
                  theme={THEME.light}
                  placeholder="Type Name"
                  onChange={handleReleaseNameChange}
                  maxLength={100}
                  disabled={isDisabled}
                />
              </div>
              <div className={classes('content-item')}>
                <Typography type={TYPOGRAPHY_TYPE.body3} className={classes('content-title')}>
                  Release Type
                </Typography>
                <Select
                  value={releaseType}
                  variant={SELECT_TYPE.compact}
                  onChange={handleReleaseTypeChange}
                  options={options}
                  placeholder="Select Type"
                  isDisabled={isDisabled}
                  rootClassName={classes('select')}
                  popperClassName={classes('select')}
                />
              </div>
            </>
          );
      }
    }, [
      handleRefreshClick,
      handleReleaseNameChange,
      handleReleaseTypeChange,
      isDisabled,
      loading,
      options,
      releaseName,
      releaseType,
    ]);

    return isOpened ? (
      <SReleaseDetailsPopup position={position} isDisabled={isDisabled}>
        {className => (
          <div className={className}>
            <div ref={rootRef} className={classes('root')}>
              <div className={classes('header')}>
                <Typography type={TYPOGRAPHY_TYPE.body1} className={classes('title')}>
                  Release Details
                </Typography>
                <Button
                  theme={THEME.light}
                  type={BUTTON_TYPE.tertiary}
                  size={BUTTON_SIZE.smallRound}
                  className={classes('close')}
                  icon="cross"
                  onClick={onClose}
                />
              </div>
              <div className={classes('content')}>{renderContent}</div>
            </div>
          </div>
        )}
      </SReleaseDetailsPopup>
    ) : null;
  }
);
