import { forwardRef, useEffect, useImperativeHandle, useState } from 'react';

import { HandleEvents } from '../helpers/event';
import { Overlay } from '../overlay';
import type { UIEventHandlers } from '../types/event';
import { useNavermaps } from '../use-navermaps';
import { omitUndefined } from '../utils/omit-undefined';

const kvoKeys = [
  'path',
  'strokeWeight',
  'strokeOpacity',
  'strokeColor',
  'strokeStyle',
  'strokeLineCap',
  'strokeLineJoin',
  'clickable',
  'visible',
  'zIndex',
  'startIcon',
  'startIconSize',
  'endIcon',
  'endIconSize',
] as const;
const kvoEvents = kvoKeys.map(key => `${key}_changed`);
const uiEvents = [
  'mousedown',
  'mouseup',
  'click',
  'dblclick',
  'rightclick',
  'mouseover',
  'mouseout',
  'mousemove',
] as const;
const events = [...uiEvents, ...kvoEvents];

type PolylineOptions = {
  /**
   * @type naver.maps.ArrayOfCoords | naver.maps.KVOArrayOfCoords | naver.maps.ArrayOfCoordsLiteral
   */
  path: naver.maps.ArrayOfCoords | naver.maps.KVOArrayOfCoords | naver.maps.ArrayOfCoordsLiteral;
  strokeWeight?: number;
  strokeOpacity?: number;
  strokeColor?: string;
  strokeStyle?: naver.maps.StrokeStyleType;
  strokeLineCap?: naver.maps.StrokeLineCapType;
  strokeLineJoin?: naver.maps.StrokeLineJoinType;
  clickable?: boolean;
  visible?: boolean;
  zIndex?: number;
  startIcon?: naver.maps.PointingIcon;
  startIconSize?: number;
  endIcon?: naver.maps.PointingIcon;
  endIconSize?: number;
};

export type Props = PolylineOptions & {
  onPathChanged?: (value: naver.maps.ArrayOfCoords) => void;
  onStrokeWeightChanged?: (value: number) => void;
  onStrokeOpacityChanged?: (value: number) => void;
  onStrokeColorChanged?: (value: string) => void;
  onStrokeStyleChanged?: (value: naver.maps.StrokeStyleType) => void;
  onStrokeLineCapChanged?: (value: naver.maps.StrokeLineCapType) => void;
  onStrokeLineJoinChanged?: (value: naver.maps.StrokeLineJoinType) => void;
  onClickableChanged?: (value: boolean) => void;
  onVisibleChanged?: (value: boolean) => void;
  onZIndexChanged?: (value: number) => void;
  onStartIconChanged?: (value: naver.maps.PointingIcon) => void;
  onStartIconSizeChanged?: (number: string) => void;
  onEndIconChanged?: (value: naver.maps.PointingIcon) => void;
  onEndIconSizeChanged?: (number: string) => void;
} & UIEventHandlers<typeof uiEvents>;

export const Polyline = forwardRef<naver.maps.Polyline, Props>(function Polyline(props, ref) {
  const { path } = props;
  const options = {
    path,
    ...Object.fromEntries(kvoKeys.filter(key => key !== 'path').map(key => [key, props[key]])),
  } as naver.maps.PolylineOptions;
  const navermaps = useNavermaps();
  const [polyline] = useState(() => new navermaps.Polyline(options));

  useImperativeHandle<naver.maps.Polyline | undefined, naver.maps.Polyline | undefined>(ref, () => polyline);

  useEffect(() => {
    const cleanOptions = omitUndefined(options);
    if (Object.keys(cleanOptions).length > 0) {
      polyline.setOptions(cleanOptions as naver.maps.PolylineOptions);
    }
  }, kvoKeys.map(key => options[key]));

  return (
    <Overlay element={polyline}>
      <HandleEvents events={events} listeners={props as any} />
    </Overlay>
  );
});
