/*

  interface idea 1:


  <Pokemon>
    <Evolution w={0}>
      
    </Evolution w={0}>
    <Evolution w={100} h={100}>
      { UI goes here }
    </Evolution>
    <Evolution w={150} h={50}>
      // weird: what if height is 100 and width is 150?
      // we match both evolutions. we're a better fit for the width of the first second, but a better fit for the height of the first.
      // we can fall back to pick the one with the closest area or aspect ratio, i guess
      { UI goes here }
    </Evolution>
    <Maximum>
      
    </Maximum>
  </Pokemon>


*/
import styled from '@emotion/styled';
import React, {
  Dispatch,
  SetStateAction,
  createContext,
  useContext,
  useEffect,
  useMemo,
  useRef,
  useState,
} from 'react';

const PokemonSizeContext = createContext<{
  size: {
    w: number;
    h: number;
  };
  setSize: Dispatch<SetStateAction<{ w: number; h: number }>>;
}>({
  size: {
    w: Infinity,
    h: Infinity,
  },
  setSize: () => {},
});

const EvolutionContainer = styled.div<{ w: number; h: number }>`
  display: none;
  flex-shrink: 0;
  width: 100%;
  height: 100%;
  @container (min-width: ${({ w }) => w}px) {
    @container (min-height: ${({ h }) => h}px) {
      display: block;
    }
  }
`;

export const Evolution = ({
  children,
  w,
  h,
}: {
  children: React.ReactNode;
  w: number;
  h: number;
}) => {
  const {
    size: { w: containerW, h: containerH },
  } = useContext(PokemonSizeContext);
  const isVisible = containerW >= w && containerH >= h;
  if (!isVisible) {
    return null;
  }
  return (
    <EvolutionContainer w={w} h={h}>
      {children}
    </EvolutionContainer>
  );
};

const PokemonContainer = styled.div`
  container-type: size;
  container-name: pokemon;
  width: 100%;
  height: 100%;
  overflow: hidden;
  display: flex;
  flex-direction: column;
`;

export default function Pokemon({
  children,
}: {
  children: React.ReactNode[] | React.ReactNode;
}) {
  const [size, setSize] = useState<{ w: number; h: number }>({
    w: Infinity,
    h: Infinity,
  });

  const sortedChildren = useMemo(() => {
    const asArray = React.Children.toArray(children);
    const finiteDimensions = Number.isFinite(size.w) && Number.isFinite(size.h);

    const valid = !finiteDimensions
      ? asArray
      : asArray.filter((c) => {
          if (typeof c === 'object' && 'props' in c) {
            return (
              size.w >= (c.props! as any).w && size.h >= (c.props! as any).h
            );
          }
          return false;
        });

    const sorted = (valid as any[]).sort((a, b) => {
      const aW = a.props.w;
      const aH = a.props.h;
      const bW = b.props.w;
      const bH = b.props.h;
      const aArea = aW * aH;
      const bArea = bW * bH;
      return bArea - aArea;
    });

    if (finiteDimensions) return sorted[0];

    // if no finite dimensions, render all children and let css/overflow hide the invalid ones.
    return sorted;
  }, [children, size]);

  const ref = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (ref.current) {
      const container = ref.current;
      const observer = new ResizeObserver((entries) => {
        const size = entries[0].contentBoxSize[0];
        setSize({ w: size.inlineSize, h: size.blockSize });
      });
      observer.observe(container);
      return () => {
        observer.unobserve(container);
        observer.disconnect();
      };
    }
  }, [ref]);

  const pokemonSizeContext = useMemo(
    () => ({
      size,
      setSize,
    }),
    [size, setSize]
  );

  return (
    <PokemonContainer ref={ref}>
      <PokemonSizeContext.Provider value={pokemonSizeContext}>
        {sortedChildren}
      </PokemonSizeContext.Provider>
    </PokemonContainer>
  );
}
