import React, {
  ReactNode,
  createContext,
  useContext,
  useEffect,
  useMemo,
  useState,
} from 'react';
import { Dimensions, ScaledSize } from 'react-native';

const guidelineBaseWidth = 350;
const guidelineBaseHeight = 680;

enum ORIENTATION {
  PORTRAIT = 'portrait',
  LANDSCAPE = 'landscape',
}

interface ScaleFunctions {
  horizontalScale: (size: number) => number;
  verticalScale: (size: number) => number;
  moderateHorizontalScale: (size: number, factor?: number) => number;
  moderateVerticalScale: (size: number, factor?: number) => number;
}

interface DimensionsContext {
  orientation: ORIENTATION;
  dimensions: ScaledSize;
  responsive: ScaleFunctions;
}

const createScaleFunctions = (dimensionsIn: ScaledSize) => {
  const horizontalScale = (size: number) =>
    (dimensionsIn.width / guidelineBaseWidth) * size;
  const verticalScale = (size: number) =>
    (dimensionsIn.height / guidelineBaseHeight) * size;
  const moderateHorizontalScale = (size: number, factor = 0.5) =>
    size + (horizontalScale(size) - size) * factor;
  const moderateVerticalScale = (size: number, factor = 0.5) =>
    size + (verticalScale(size) - size) * factor;

  return {
    horizontalScale,
    verticalScale,
    moderateHorizontalScale,
    moderateVerticalScale,
  };
};

const DimensionsContext = createContext<DimensionsContext | undefined>(
  undefined,
);

const DimensionsProvider = ({ children }: { children: ReactNode }) => {
  const [dimensions, setDimensions] = useState(Dimensions.get('window'));

  const orientation = useMemo(() => {
    return dimensions.width > dimensions.height
      ? ORIENTATION.LANDSCAPE
      : ORIENTATION.PORTRAIT;
  }, [dimensions]);

  const responsiveScale = createScaleFunctions(dimensions);

  useEffect(() => {
    const onChange = ({ window }: { window: ScaledSize }) => {
      setDimensions(window);
    };

    const subscription = Dimensions.addEventListener('change', onChange);

    return () => {
      subscription.remove();
    };
  }, []);

  return (
    <DimensionsContext.Provider
      value={{
        orientation,
        dimensions,
        responsive: responsiveScale,
      }}>
      {children}
    </DimensionsContext.Provider>
  );
};

const useDimensions = (): DimensionsContext => {
  const context = useContext(DimensionsContext);
  if (!context) {
    throw new Error('useDimensions must be used within a DimensionsProvider');
  }
  return context;
};

export { ORIENTATION, DimensionsProvider, useDimensions };
