import React, {
  useContext,
  useRef,
  type PropsWithChildren,
  useEffect,
  useState,
  useMemo,
  useCallback,
} from 'react';
import styled from 'styled-components';
import { useLocation } from 'react-router-dom';

import type { WithConditions } from '@redocly/config';

import { Marker } from '@redocly/theme/components/Marker/Marker';
import {
  CodeWalkthroughControlsStateContext,
  CodeWalkthroughStepsContext,
} from '@redocly/theme/core/contexts';

export type CodeStepProps = WithConditions<{
  id: string;
  heading?: string;
}>;

export function CodeStep({
  id,
  heading,
  when,
  unless,
  children,
}: PropsWithChildren<CodeStepProps>) {
  const location = useLocation();
  const compRef = useRef<HTMLDivElement | null>(null);
  const markerRef = useRef<HTMLDivElement | null>(null);

  const { areConditionsMet } = useContext(CodeWalkthroughControlsStateContext);
  const {
    activeStep,
    setActiveStep,
    markers,
    registerStep,
    removeStep,
    registerMarker,
    removeMarker,
    lockObserver,
    filtersElementRef,
  } = useContext(CodeWalkthroughStepsContext);
  const isActive = activeStep === id;

  const [scrollMarginTop, setScrollMarginTop] = useState(0);
  const marker = useMemo(() => markers[id], [markers, id]);
  const isVisible = useMemo(
    () => areConditionsMet({ when, unless }),
    [areConditionsMet, when, unless],
  );

  const handleActivateStep = useCallback(() => {
    if (lockObserver) {
      lockObserver.current = true;

      if (markerRef.current) {
        markerRef.current.scrollIntoView({ behavior: 'smooth', block: 'start' });
      }

      setActiveStep(id);
      setTimeout(() => {
        lockObserver.current = false;
      }, 1000);
    }
  }, [setActiveStep, lockObserver, id]);

  const handleRegisterMarker = useCallback(
    (element: HTMLElement) => registerMarker(id, element),
    [registerMarker, id],
  );

  const handleRemoveMarker = useCallback(
    (element: HTMLElement) => removeMarker(id, element),
    [removeMarker, id],
  );

  useEffect(() => {
    // If the step is active during navigation or first render, scroll to it
    if (!isActive) return;
    const timer = setTimeout(handleActivateStep, 5);
    return () => clearTimeout(timer);
    // Ignore dependency array because we only need to run this once
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [location.pathname]);

  useEffect(() => {
    const currentCompRef = compRef.current;
    if (currentCompRef && isVisible) {
      currentCompRef
        .querySelectorAll<HTMLElement>('a, button, input, textarea, select, [tabindex]')
        .forEach((el) => {
          el.setAttribute('tabindex', '-1');
        });

      registerStep(id, currentCompRef);
    }

    const filtersElementHeight = filtersElementRef?.current?.clientHeight || 0;
    const navbarHeight = document.querySelector('nav')?.clientHeight || 0;
    setScrollMarginTop(filtersElementHeight + navbarHeight + 10);

    return () => {
      removeStep(id);
    };
  }, [filtersElementRef, registerStep, removeStep, id, isVisible]);

  if (!isVisible) {
    return null;
  }

  return (
    <>
      {marker && (
        <Marker
          ref={markerRef}
          marker={marker}
          registerMarker={handleRegisterMarker}
          removeMarker={handleRemoveMarker}
          dataAttribures={{
            'data-step-id': id,
            'data-step-active': isActive,
          }}
        />
      )}
      <StepWrapper
        data-component-name="Markdoc/CodeWalkthrough/CodeStep"
        ref={compRef}
        isActive={isActive}
        scrollMarginTop={scrollMarginTop}
        onClick={handleActivateStep}
        onFocus={handleActivateStep}
        tabIndex={0}
        className="code-step-wrapper"
      >
        <StepContent isActive={isActive}>
          {heading ? <StepHeading>{heading}</StepHeading> : null}
          {children}
        </StepContent>
      </StepWrapper>
    </>
  );
}

const StepContent = styled.div<{ isActive: boolean }>`
  margin: var(--spacing-xs) 0px var(--spacing-xs) calc(var(--spacing-unit) * 3.5);
  padding: var(--spacing-md) var(--spacing-lg);
  background: ${({ isActive }) => (isActive ? 'var(--layer-color)' : 'none')};
  border-radius: var(--border-radius);

  &:hover {
    background-color: ${({ isActive }) =>
      isActive ? 'var(--code-step-bg-active-hover)' : 'var(--code-step-bg-hover)'};
  }
`;

const StepHeading = styled.p`
  font-weight: var(--font-weight-semibold);
`;

export const StepWrapper = styled.div<{ isActive: boolean; scrollMarginTop: number }>`
  position: relative;
  scroll-margin-top: ${({ scrollMarginTop }) => scrollMarginTop}px;

  &::before {
    content: '';
    position: absolute;
    width: 6px;
    height: 100%;
    background-color: ${({ isActive }) =>
      isActive ? 'var(--code-step-vertical-line-bg-active)' : 'none'};
    border-radius: var(--border-radius-lg);
  }
  &:hover::before {
    background-color: ${({ isActive }) =>
      isActive
        ? 'var(--code-step-vertical-line-bg-active)'
        : 'var(--code-step-vertical-line-bg-hover)'};
  }

  &:hover::before {
    width: ${({ isActive }) => (isActive ? '8px' : '6px')};
  }

  &:hover ${StepContent} {
    background-color: ${({ isActive }) =>
      isActive ? 'var(--code-step-bg-active-hover)' : 'var(--code-step-bg-hover)'};
  }
`;
