import React, { useRef, useState, useEffect } from 'react';
import { CSSTransition } from 'react-transition-group';
import cn from 'classnames';
import { createPortal } from 'react-dom';

import { useWindowScroll } from 'react-use';
import { ViewTypes } from '@veeqo/ui/dist/components/View/types';
import useDropdown from 'hooks/useDropdown';

import Confirmation from './Confirmation';
import InfoPill from './Pills/Info';
import DarkPill from './Pills/Success/Dark';
import UnsavedChangesPill from './Pills/UnsavedChanges';

import EditDropdown from './EditDropdown';
import OptionsDropdown from './OptionsDropdown';

import { Container, View } from './styled';
import {
  Shared, ViewType, ViewClassNames, ViewHandlers, CommonViewPropTypes,
} from '../types';

import { DEFAULT_CONTENT_WIDTH as DEFAULT_EDIT_DROPDOWN_CONTENT_WIDTH } from './EditDropdown/styled';
import { getOptionsDropdownWidth } from './OptionsDropdown/styled';

const DEFAULT_DROPDOWN_OFFSET = 24;

const getDropdownStyle = (
  referenceElementRect: DOMRect,
  dropdownWidth: number,
  scrollY: number,
): React.CSSProperties => {
  const isOutsideTheScreenOnRight = referenceElementRect.right > window.innerWidth;
  const isOutsideTheScreenOnLeft = referenceElementRect.left < 0;

  let { left } = referenceElementRect;
  if (isOutsideTheScreenOnRight) left = window.innerWidth - referenceElementRect.width - 40;
  // 72 is dropdown width
  if (isOutsideTheScreenOnLeft) left = 40 + 72;

  const isFitOnRight = (
    window.innerWidth - (left + DEFAULT_DROPDOWN_OFFSET) > dropdownWidth
  );

  return {
    position: 'absolute',
    top: referenceElementRect.bottom - 12 + scrollY,
    left: isFitOnRight
      ? left + DEFAULT_DROPDOWN_OFFSET
      : (
        left - dropdownWidth
        + referenceElementRect.width - DEFAULT_DROPDOWN_OFFSET
      ),
  };
};

export type ViewElementPropsType = CommonViewPropTypes & ViewHandlers & {
  className?: string;
  e2eClassName?: string;
  view: ViewType;
  updateChunks: () => void;
};

const generateClassNames = (prefix?: string): ViewClassNames => ({
  view: prefix ? `${prefix}-view` : undefined,
  optionsDropdown: prefix ? `${prefix}-view-options-dropdown` : undefined,
  savedViewEditDropdown: prefix ? `${prefix}-saved-view-edit-dropdown` : undefined,
  draftViewEditDropdown: prefix ? `${prefix}-draft-view-edit-dropdown` : undefined,
  deleteConfirmation: prefix ? `${prefix}-delete-view-confirmation` : undefined,
  draftViewInfoPill: prefix ? `${prefix}-draft-view-info-pill` : undefined,
  successNotification: prefix ? `${prefix}-view-success-notification` : undefined,
  unsavedChangesPill: prefix ? `${prefix}-view-unsaved-changes-pill` : undefined,
});

const ViewElement = ({
  className,
  e2eClassName,
  view,
  activeViewKey,
  includedFilters,
  hasUnsavedChanges,
  pageName,
  onSave,
  onDelete,
  updateChunks,
  handleDuplicateView,
  handleChangeActiveView,
  handleDiscardUnsavedChanges,
  handleSaveToCurrentView,
  handleCreateCustomViewWithCurrentFilters,
  handleSetDefault,
}: ViewElementPropsType) => {
  const classNames = generateClassNames(className);
  const e2eClassNames = generateClassNames(e2eClassName);
  const { shouldShowDropdown, toggleShouldShowDropdown, setShouldShowDropdown } = useDropdown();
  const [shouldShowDeleteConfirmation, setShouldShowDeleteConfirmation] = useState(false);
  const [shouldShowEditDropdown, setShouldShowEditDropdown] = useState(false);
  const [shouldShowCreateDropdown, setShouldShowCreateDropdown] = useState(false);
  const [successNotificationMessage, setSuccessNotificationMessage] = useState<string | null>(
    null,
  );

  const referenceElement = useRef<HTMLDivElement>(null);
  const referenceElementRect = referenceElement.current?.getBoundingClientRect();
  const { y: scrollY } = useWindowScroll();

  const [viewName, setViewName] = useState(view.label || '');
  const [isShared, setIsShared] = useState<Shared>(view.shared ? 'true' : 'false');
  const [isDefault, setIsViewDefault] = useState(view.default);
  const isDraft = view.type === ViewTypes.draft;
  const isSaved = view.type === ViewTypes.saved;
  const isActive = view.key === activeViewKey;

  const handleCreate = (editable: boolean) => {
    updateChunks();
    handleSetDefault(view.key, isDefault);
    onSave(view.key, viewName, editable, isShared === 'true');

    setShouldShowCreateDropdown(false);
    setSuccessNotificationMessage(`'${viewName}' view created`);

    setTimeout(() => {
      setSuccessNotificationMessage(null);
    }, 5000);
  };
  const handleSave = (editable: boolean) => {
    updateChunks();
    handleSetDefault(view.key, isDefault);
    onSave(view.key, viewName, editable, isShared === 'true');

    setShouldShowEditDropdown(false);
    setSuccessNotificationMessage(`'${viewName}' view updated`);

    setTimeout(() => {
      setSuccessNotificationMessage(null);
    }, 5000);
  };
  const handleMakeDefaultView = () => {
    setIsViewDefault(true);
    handleSetDefault(view.key, true);

    setShouldShowDropdown(false);
    setSuccessNotificationMessage(`'${viewName}' is now your default view`);

    setTimeout(() => {
      setSuccessNotificationMessage(null);
    }, 5000);
  };
  const handleDelete = () => {
    onDelete(view.key);

    if (shouldShowEditDropdown) setShouldShowEditDropdown(shouldShowEditDropdown);
    if (shouldShowDeleteConfirmation) setShouldShowDeleteConfirmation(false);
  };

  useEffect(() => {
    setIsViewDefault(view.default);
  }, [view.default]);

  return (
    <>
      <Container className="veeqo-react-view" ref={referenceElement} key={view.key}>
        <View
          e2eClassName={cn(classNames?.view, e2eClassNames?.view)}
          active={isActive}
          id={view.key}
          name={viewName}
          type={view.type}
          onClick={(viewKey) => (viewKey !== activeViewKey ? handleChangeActiveView(viewKey) : {})}
          onDelete={handleDelete}
          onEdit={toggleShouldShowDropdown}
          onOptions={toggleShouldShowDropdown}
        />
        <Confirmation
          className={cn(classNames?.deleteConfirmation, e2eClassNames?.deleteConfirmation)}
          shouldShow={shouldShowDeleteConfirmation}
          viewName={viewName}
          onCancel={() => setShouldShowDeleteConfirmation(false)}
          onDelete={() => handleDelete()}
        />
        <CSSTransition in={isDraft && isActive && !shouldShowCreateDropdown} timeout={200} classNames="notification" unmountOnExit>
          <InfoPill
            className={cn(classNames?.draftViewInfoPill, e2eClassNames?.draftViewInfoPill)}
            pageName={pageName}
            onClick={() => setShouldShowCreateDropdown(true)}
          />
        </CSSTransition>
        <CSSTransition
          in={successNotificationMessage !== null}
          timeout={200}
          classNames="notification"
          unmountOnExit
        >
          <DarkPill
            viewName={viewName}
            className={cn(classNames?.successNotification, e2eClassNames?.successNotification)}
            message={successNotificationMessage || ''}
            onClick={() => setSuccessNotificationMessage(null)}
          />
        </CSSTransition>
        <CSSTransition
          in={hasUnsavedChanges && isActive && isSaved}
          timeout={200}
          classNames="notification"
          unmountOnExit
        >
          <UnsavedChangesPill
            className={cn(classNames?.unsavedChangesPill, e2eClassNames?.unsavedChangesPill)}
            viewName={viewName}
            onDiscard={handleDiscardUnsavedChanges}
            handleSaveToCurrentView={() => handleSaveToCurrentView(view.key, view.editable || true)}
            handleCreateCustomViewWithCurrentFilters={handleCreateCustomViewWithCurrentFilters}
          />
        </CSSTransition>
      </Container>
      {shouldShowDropdown && createPortal(
        <div
          style={referenceElementRect
            ? getDropdownStyle(
              referenceElementRect,
              getOptionsDropdownWidth(view.type === 'fixed', isDefault),
              scrollY,
            )
            : undefined}
        >
          <OptionsDropdown
            className={cn(classNames?.optionsDropdown, e2eClassNames?.optionsDropdown)}
            shouldShowDropdown={shouldShowDropdown}
            view={view}
            onMakeDefault={handleMakeDefaultView}
            onDelete={() => setShouldShowDeleteConfirmation(true)}
            onDuplicate={() => handleDuplicateView(view.key)}
            onEdit={() => {
              setShouldShowDropdown(false);
              setShouldShowEditDropdown(true);
            }}
            handleCloseDropdown={() => (
              shouldShowDeleteConfirmation
                ? {}
                : setShouldShowDropdown(false)
            )}
          />
        </div>,
        document.body,
      )}
      {shouldShowEditDropdown && createPortal(
        <div
          style={referenceElementRect
            ? getDropdownStyle(referenceElementRect, DEFAULT_EDIT_DROPDOWN_CONTENT_WIDTH, scrollY)
            : undefined}
        >
          <EditDropdown
            className={cn(classNames?.savedViewEditDropdown, e2eClassNames?.savedViewEditDropdown)}
            viewName={viewName}
            includedFilters={includedFilters}
            shouldShowDropdown={shouldShowEditDropdown}
            headerText="Edit view"
            shared={isShared}
            isDefault={isDefault}
            onChange={(value) => setViewName(value)}
            onCloseDropdown={() => {
              setShouldShowEditDropdown(false);
              setViewName(view.label);
              setIsViewDefault(view.default);
              setIsShared(view.shared ? 'true' : 'false');
            }}
            onSave={(editable) => handleSave(editable)}
            onDelete={() => setShouldShowDeleteConfirmation(true)}
            onSharingOptionChange={setIsShared}
            handleChangeIsDefault={setIsViewDefault}
          />
        </div>,
        document.body,
      )}
      {shouldShowCreateDropdown && createPortal(
        <div
          style={referenceElementRect
            ? getDropdownStyle(referenceElementRect, DEFAULT_EDIT_DROPDOWN_CONTENT_WIDTH, scrollY)
            : undefined}
        >
          {/*
            Reusing same dropdown for drafts as it looks the same with few minor tweaks in behavior
          */}
          <EditDropdown
            className={cn(classNames?.draftViewEditDropdown, e2eClassNames?.draftViewEditDropdown)}
            viewName={viewName}
            includedFilters={includedFilters}
            shouldShowDropdown={shouldShowCreateDropdown}
            headerText="Create new view"
            shared={isShared}
            isDefault={isDefault}
            onChange={(value) => setViewName(value)}
            onCloseDropdown={() => setShouldShowCreateDropdown(false)}
            onSave={(editable) => handleCreate(editable)}
            onDelete={() => setShouldShowDeleteConfirmation(true)}
            onSharingOptionChange={setIsShared}
            handleChangeIsDefault={setIsViewDefault}
          />
        </div>,
        document.body,
      )}
    </>
  );
};

export default ViewElement;
