import React, { useMemo } from 'react';
import { useEffect, useImperativeHandle } from 'react';

import { animated as a, Controller, useSpring } from '@react-spring/web';
import { useWebMergeStyles } from '@cleartrip/ct-design-style-manager';

import { IAnimatedComponentProps, IAnimationOptions } from '../type';
import { IAnimatedStyleClass } from '../AnimatedStyle/IAnimatedStyleClass';

function getAnimatedStyle(animated: IAnimatedStyleClass) {
  if (animated.getWebIsSequence()) {
    const animatedStyles = animated.getWebAnimatedSequenceStyles();
    const firstAnimatedStyle = animatedStyles[0][0];

    if (!firstAnimatedStyle) {
      return null;
    }

    return firstAnimatedStyle.getWebAnimatedStyle();
  }

  return animated.getWebAnimatedStyle() ?? null;
}

function getSpringRef(animated: IAnimatedStyleClass) {
  return animated.getWebSpringRef() ?? null;
}

function getInitialSpringValue(animated: IAnimatedStyleClass) {
  if (animated.getWebIsSequence()) {
    const animatedStyles = animated.getWebAnimatedSequenceStyles();

    const initialSpringValue: Record<string, unknown> = {};

    for (const [animatedStyle] of animatedStyles) {
      const animatedStyleValue = animatedStyle.getWebAnimatedStyle();

      if (animatedStyleValue?.from) {
        // Only assign properties that don't already exist in mergedFrom
        Object.keys(animatedStyleValue.from).forEach((key) => {
          if (!(key in initialSpringValue)) {
            initialSpringValue[key] = animatedStyleValue.from?.[key];
          }
        });
      }
    }

    return initialSpringValue;
  }

  return animated.getWebAnimatedStyle()?.from ?? {};
}

const SpringContainer = ({ children, animated, styleConfig = {}, onTransitionEnd }: IAnimatedComponentProps) => {
  const { from, to, config: defaultConfig } = getAnimatedStyle(animated) ?? {};
  const { springConfig: defaultSpringConfig = {}, delay: defaultDelay } = defaultConfig ?? {};
  const { root: rootStyles = [] } = styleConfig || {};

  const handleTransitionEnd = () => {
    onTransitionEnd?.();
  };

  const [springStyles, springController] = useSpring(() => ({
    from,
    to,
    config: defaultSpringConfig,
    delay: defaultDelay,
  }));

  // eslint-disable-next-line react-hooks/exhaustive-deps
  const springAnimation = useMemo(() => new Controller({ ...getInitialSpringValue(animated) }), []);

  useEffect(() => {
    if (defaultConfig?.immediate) {
      getSpringRef(animated)?.current?.start?.();
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  const handleSequenceAnimationStart = () => {
    const animatedStyles = animated.getWebAnimatedSequenceStyles();

    const startAnimationAtIndex = (index: number) => {
      if (index >= animatedStyles.length) {
        handleTransitionEnd();
        return;
      }

      const animatedStyle = animatedStyles[index][0];
      const animatedStyleValue = animatedStyle.getWebAnimatedStyle();

      const delay = animatedStyleValue?.config?.delay ?? defaultDelay;

      const springConfig = animatedStyleValue?.config?.springConfig ?? {};

      springAnimation.start?.({
        from: animatedStyleValue?.from,
        to: animatedStyleValue?.to,
        config: springConfig,
        delay,
        onRest: () => {
          startAnimationAtIndex(index + 1);
        },
      });
    };

    startAnimationAtIndex(0);
  };

  useImperativeHandle(
    getSpringRef(animated),
    () => {
      return {
        start(animatedStyle?: IAnimationOptions) {
          if (animated.getWebIsSequence()) {
            handleSequenceAnimationStart();
            return;
          }

          if (animatedStyle) {
            const {
              from = {},
              to = {},
              config: { springConfig = defaultSpringConfig, delay } = {},
            } = animatedStyle ?? {};
            springController.start?.({
              from,
              to,
              config: springConfig,
              delay,
              onRest: handleTransitionEnd,
            });
          } else {
            springController.start?.({
              to,
              config: defaultSpringConfig,
              delay: defaultDelay,
              onRest: handleTransitionEnd,
            });
          }
        },
      };
    },
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [],
  );

  const mergedStyles = useWebMergeStyles([...rootStyles], [rootStyles]);

  return (
    <a.div className={mergedStyles} style={animated.getWebIsSequence() ? springAnimation.springs : springStyles}>
      {children}
    </a.div>
  );
};

export default React.memo(SpringContainer);
