import { Map, YMaps } from "@pbe/react-yandex-maps";
import type {
  ReactElement, FC, PropsWithChildren, NamedExoticComponent,
} from "react";
import {
  useContext, memo, useEffect, useState, useRef,
} from "react";
import type { IMapState, IMapOptions, Clusterer } from "yandex-maps";
import type {
  TMapContext, TCluster, TLangs, TAPI, TMapInstance,
} from "../config/types";
import { MapContext } from "../context";
import { MapItems } from "../ui/MapItems";
import type { TMapItemsProps } from "../ui/MapItems";
import { MapModules } from "../ui/MapModules";
import type { TMapModulesProps } from "../ui/MapModules";
import { MapPanel } from "../ui/MapPanel";
import type { TMapPanelProps } from "../ui/MapPanel";

const MemoModules: NamedExoticComponent<TMapModulesProps> = memo((props) => <MapModules {...props} />);
MemoModules.displayName = "MapModules";

const MemoItems: NamedExoticComponent<TMapItemsProps> = memo((props) => <MapItems {...props} />);
MemoItems.displayName = "MapItems";

const MemoPanel: NamedExoticComponent<TMapPanelProps> = memo((props) => <MapPanel {...props} />);
MemoPanel.displayName = "MapPanel";

/**
 * Внутренний компонент карты

 * @returns {ReactElement}
 */
const YandexMap: FC<PropsWithChildren & {
  onReady: (instance: TAPI, clusters: Clusterer[]) => void;
  onError: (err: Error) => void;
  state: IMapState;
  options: IMapOptions;
  query: {
    apikey: string;
    lang: TLangs;
  };
  clusters: TCluster[];
}> = ({
  onReady,
  onError,
  children,
  query,
  state,
  clusters,
  options,
}): ReactElement => {

  const { instanceRef, getCN } = useContext<TMapContext>(MapContext);

  const [ apiInstance, setApiInstance ] = useState<TAPI>(null);
  const [ clustersInstance, setClustersInstance ] = useState<Clusterer[] | null>(null);
  const isReadyCalled = useRef<boolean>(false);

  const onClustersReady = (clusters: Clusterer[]) => {
    setClustersInstance(clusters);
  };

  const onMapReady = (ymaps: TAPI) => {
    setApiInstance(ymaps);
  };

  const saveInstance = (instance: TMapInstance) => {
    if (instance) { // can be null
      instanceRef.current = instance;
    }
  };

  useEffect(() => {
    if (apiInstance && clustersInstance && !isReadyCalled.current && typeof onReady === "function") {
      isReadyCalled.current = true;
      onReady(apiInstance, clustersInstance);
    }
  }, [ apiInstance, clustersInstance, onReady ]);

  return (
    <YMaps query={query}>
      <Map
        className={getCN("map")}
        state={state}
        defaultState={state}
        defaultOptions={options}
        options={options}
        onLoad={onMapReady}
        onError={onError}
        width={"100%"}
        height={"100%"}
        instanceRef={saveInstance}>
        <MemoItems onReady={onClustersReady} clusters={clusters}>
          {children}
        </MemoItems>
      </Map>
    </YMaps>
  );

};

export default YandexMap;
