import {
  FullscreenControl,
  GeolocationControl,
  RulerControl,
  SearchControl,
  TrafficControl,
  ZoomControl,
  TypeSelector,
} from "@pbe/react-yandex-maps";
import type { FC, PropsWithChildren, ReactNode } from "react";
import { useMemo } from "react";
import { mapDefaults } from "../config";
import type { IMap, TAPI, TUserFactory } from "../config/types";
import { getLayoutConstructor } from "../lib";
import { isNeedFactory } from "../lib/modules";

export type TMapModulesProps = Pick<IMap, "modules"> & PropsWithChildren<{
  ymaps: TAPI;
}>;

type TModuleName = keyof TMapModulesProps["modules"];
type TModuleProps = TMapModulesProps["modules"][TModuleName];

const MapModule: FC<{
  name: TModuleName;
  data: TModuleProps;
  ymaps: TAPI;
}> = ({ name = "", data = {}, ymaps }): ReactNode => {

  const computedProps = useMemo(() => {
    const props: {
      [key: string]: unknown;
    } = { ...data };
    // Adds factory instance from user field
    if (isNeedFactory(data)) {
      const { html, methods, callback } = (data as TUserFactory).factory;
      props.options = {
        ...(props?.options ?? {}),
        layout: getLayoutConstructor(ymaps, html, callback, data, methods),
      };
      delete props.factory;
    }
    return props;
  }, [ data ]);


  switch (name) {
    case "FullscreenControl":
      return <FullscreenControl {...computedProps} />;
    case "GeolocationControl":
      return <GeolocationControl {...computedProps} />;
    case "RulerControl":
      return <RulerControl {...computedProps} />;
    case "SearchControl":
      return <SearchControl {...computedProps} />;
    case "TrafficControl":
      return <TrafficControl {...computedProps} />;
    case "TypeSelector":
      return <TypeSelector {...computedProps} />;
    case "ZoomControl":
      return <ZoomControl {...computedProps} />;
    default:
      return null;
  }
};

/**
 * Внутренний компонент модулей карты
 * @returns {ReactElement}
 */
const MapModules: FC<TMapModulesProps> = ({ children, modules = mapDefaults.modules, ymaps }: TMapModulesProps): ReactNode => {

  const elements = useMemo<[ TModuleName, TModuleProps ][]>(() => {
    return Object.entries(modules || {})
      .filter(([ name, props ]) => {
        return !!name && !!props;
      }) as [ TModuleName, TModuleProps ];
  }, [ modules ]);

  return (
    <>
      {elements.map(([ name, props ]: [ TModuleName, TModuleProps ]) => {
        return <MapModule ymaps={ymaps} name={name} data={props} key={name} />;
      })}
      {children}
    </>
  );

};

export {
  MapModules,
};
