import { useCallback, useEffect, useRef } from "react";

import {
  createSmartlinkMessage,
  SmartlinkMessage,
  SmartlinkMessageMap,
  SmartlinkParentAPI,
  SmartlinkSubscriber,
  UseSmartlinkParentMessagingOptions,
} from "./types";

/**
 * @file useSmartlinkParentMessaging.ts
 * @description Parent-side hook for smartlink iframes.
 *
 * Responsibilities this hook owns:
 *   - Deriving and enforcing pluginOrigin on all sends and receives
 *   - Origin check on every incoming message (no route registry — origin only)
 *   - Applying SMARTLINK_RESIZE to the iframe element (with optional onResize override)
 *   - Emitting all messages to subscribers with an isValid flag
 *   - Exposing named senders as conveniences
 *
 * Responsibilities this hook does NOT own:
 *   - Deciding what to send when SMARTLINK_READY fires
 *   - Handling step completion or cancellation
 *   - Error handling beyond passing SMARTLINK_ERROR to subscribers
 *
 * All of the above are consumer responsibilities.
 */
export const useSmartlinkParentMessaging = ({
  pluginUrl,
  iframeRef,
  onResize,
}: UseSmartlinkParentMessagingOptions): SmartlinkParentAPI => {
  const pluginOrigin = pluginUrl ? new URL(pluginUrl).origin : null;

  /*------------------------------ SUBSCRIBERS STORE --------------------------------*/

  const subscribersRef = useRef<Set<SmartlinkSubscriber>>(new Set());

  const subscribe = useCallback((handler: SmartlinkSubscriber) => {
    subscribersRef.current.add(handler);
    return () => {
      subscribersRef.current.delete(handler);
    };
  }, []);

  const subscribeTo = useCallback(
    <K extends keyof SmartlinkMessageMap>(
      type: K,
      handler: (
        message: SmartlinkMessage<K>,
        event: MessageEvent<SmartlinkMessage>,
        isValid: boolean,
      ) => void,
    ) => {
      return subscribe((message, event, isValid) => {
        if (message.type !== type) return;
        handler(message as SmartlinkMessage<K>, event, isValid);
      });
    },
    [subscribe],
  );

  const emitToSubscribers = useCallback(
    (
      msg: SmartlinkMessage,
      event: MessageEvent<SmartlinkMessage>,
      isValid: boolean,
    ) => {
      subscribersRef.current.forEach((fn) => fn(msg, event, isValid));
    },
    [],
  );

  /*---------------------------------- SENDER ---------------------------------------*/

  const sendMessage = useCallback(
    (message: SmartlinkMessage) => {
      if (!iframeRef.current?.contentWindow || !pluginOrigin) return;
      iframeRef.current.contentWindow.postMessage(message, pluginOrigin);
    },
    [iframeRef, pluginOrigin],
  );

  const sendInit = useCallback(
    (requestId?: string) => {
      sendMessage(
        createSmartlinkMessage("SMARTLINK_INIT", undefined, requestId),
      );
    },
    [sendMessage],
  );

  /*--------------------------------- RECEIVER --------------------------------------*/

  useEffect(() => {
    const iframe = iframeRef.current;

    const handleMessage = (event: MessageEvent<SmartlinkMessage>) => {
      if (event.origin !== pluginOrigin) return;
      const { data } = event;
      if (!data || typeof data !== "object" || !("type" in data)) return;

      /*
       * Smartlink uses origin check only — no route registry revalidation.
       * isValid is true as long as the origin matches pluginOrigin.
       */
      const isValid = true;

      if (data.type === "SMARTLINK_RESIZE") {
        const { height, width } =
          (data.payload as SmartlinkMessageMap["SMARTLINK_RESIZE"]) ?? {};
        if (typeof height === "number" && iframe) {
          const requested = { height, width };
          const override = onResize?.(requested);
          const resolved = override ?? requested;
          iframe.style.height = `${resolved.height}px`;
          if (resolved.width !== undefined) {
            iframe.style.width = `${resolved.width}px`;
          }
        }
      }

      emitToSubscribers(data, event, isValid);
    };

    window.addEventListener("message", handleMessage);
    return () => {
      window.removeEventListener("message", handleMessage);
    };
  }, [pluginUrl, pluginOrigin, iframeRef, onResize, emitToSubscribers]);

  /*-------------------------------- EXPORT API -------------------------------------*/

  return { sendMessage, sendInit, subscribe, subscribeTo };
};
