import { ReaderObject } from "../../adapters/reader/lisio-reader-adapter";
import LisioBroadcastChannelMessagesNames from "../../enums/lisio-broadcast-channel-message-names";
import LisioBroadcastChannelNames from "../../enums/lisio-broadcast-channel-names";

class LisioBroadcastChannelController {
  private _broadcastChannel: BroadcastChannel;
  private _channelMessageHandlers: Map<
    string,
    (datas: string) => Promise<void>
  > = new Map<string, (datas: string) => Promise<void>>();
  private _channels: Map<string, chrome.runtime.Port> = new Map<
    string,
    chrome.runtime.Port
  >();

  constructor(channelName: LisioBroadcastChannelNames) {
    this._broadcastChannel = new BroadcastChannel(channelName);
    if (__BUILD_TARGET__ === "extension") {
      if (window.location.pathname === "/reading-mode.html") {
        const channelReading = chrome.runtime.connect(chrome.runtime.id, {
          name: "broadcast-channel-reading",
        });
        // channelReading.postMessage("LOTR > HP");
        this._channels.set("broadcast-channel-reading", channelReading);
        channelReading.onMessage.addListener(
          (datas: { name: string; datas: string }) => {
            const message: { name: string; datas: string } = datas;
            if (message.name == undefined) {
              throw new Error(
                `Message name is not defined : ${datas.name} on channel : ${channelReading.name}`,
              );
            } else {
              const handler = this._channelMessageHandlers.get(message.name);
              if (handler != undefined) {
                handler(message.datas);
              }
            }
          },
        );
      } else {
        const siteChannel = chrome.runtime.connect(chrome.runtime.id, {
          name: "broadcast-channel-site",
        });
        window.addEventListener("lisio_open_channels", () => {
          const siteChannel = chrome.runtime.connect(chrome.runtime.id, {
            name: "broadcast-channel-site",
          });
          this._channels.set("broadcast-channel-site", siteChannel);
          // siteChannel.postMessage("Fuyez pauvre fous !!!");
        });
        this._channels.set("broadcast-channel-site", siteChannel);
        // siteChannel.postMessage("Fuyez pauvre fous !!!");
        siteChannel.onMessage.addListener(
          (datas: { name: string; datas: string }) => {
            const message: { name: string; datas: string } = datas;
            if (message.name == undefined) {
              throw new Error(
                `Message name is not defined : ${datas.name} on channel : ${siteChannel.name}`,
              );
            } else {
              const handler = this._channelMessageHandlers.get(message.name);
              if (handler != undefined) {
                handler(message.datas);
              }
            }
          },
        );
      }
    } else {
      this._broadcastChannel.addEventListener(
        "message",
        (event: MessageEvent) => {
          const { name, datas } = event.data as { name: string; datas: string };
          const messageHandler = this._channelMessageHandlers.get(name);
          if (messageHandler != undefined) {
            console.log(name, datas);
            messageHandler(datas);
          }
        },
      );
    }
  }

  public get broadcastChannel(): BroadcastChannel {
    return this._broadcastChannel;
  }

  public attachHandler(
    name: string,
    handler: (datas: string) => Promise<void>,
  ) {
    this._channelMessageHandlers.set(name, handler);
  }

  public removeHandler(name: string) {
    this._channelMessageHandlers.delete(name);
  }

  public sendAnyoneThereMessage(datas: { value: string }) {
    this.postMessageToChannel(
      LisioBroadcastChannelMessagesNames.BCMR_ANYONE_THERE,
      datas,
    );
  }

  public sendCloseMessage() {
    this.postMessageToChannel(
      LisioBroadcastChannelMessagesNames.BCMR_CLOSE,
      {},
    );
  }

  public sendGoBackMessage() {
    this.postMessageToChannel(
      LisioBroadcastChannelMessagesNames.BCMS_GO_BACK,
      {},
    );
  }

  public sendInitHTMLMessage(datas: {
    back: string;
    isMobile: boolean;
    texts: ReaderObject[];
    styles: Record<string, string>;
    domain: string;
    base64s: Record<string, string>;
    calendarLang: "fr" | "en";
  }) {
    this.postMessageToChannel(
      LisioBroadcastChannelMessagesNames.BCMR_INIT_HTML,
      datas,
    );
  }

  public sendModifiedTextMessage(
    lisioIndex: string,
    iChild: number,
    textContent: string,
  ) {
    this.postMessageToChannel(
      LisioBroadcastChannelMessagesNames.BCMR_MODIFIED_TEXT,
      { index: lisioIndex, childIndex: iChild, text: textContent },
    );
  }

  public sendNavResponseMessage(value: boolean) {
    this.postMessageToChannel(
      LisioBroadcastChannelMessagesNames.BCMS_NAV_RESPONSE,
      { value },
    );
  }

  public sendNewImageMessage(datas: {
    position: number;
    src: string;
    base64: string;
    width: number;
    height: number;
  }) {
    this.postMessageToChannel(
      LisioBroadcastChannelMessagesNames.BCMR_NEW_IMAGE,
      datas,
    );
  }

  public sendNewNodesMessage(
    toSend: {
      name: string;
      index: number;
      content: string;
      classes: string;
      position: number;
      alreadyExists: boolean;
      clickable: number;
    }[],
  ) {
    this.postMessageToChannel(
      LisioBroadcastChannelMessagesNames.BCMR_NEW_NODES,
      {
        toSend,
      },
    );
  }

  public sendOnChangeMessage(toSend: { target: string; value: string }) {
    this.postMessageToChannel(
      LisioBroadcastChannelMessagesNames.BCMS_ON_CHANGE,
      toSend,
    );
  }

  public sendOnClickMessage(toSend: {
    target: string;
    isShadowRootCalendar: boolean;
  }) {
    this.postMessageToChannel(
      LisioBroadcastChannelMessagesNames.BCMS_ON_CLICK,
      toSend,
    );
  }

  public sendReadyMessage() {
    this.postMessageToChannel(
      LisioBroadcastChannelMessagesNames.BCMS_READY,
      {},
    );
  }

  public sendRefreshCalendarMessage(datas: {
    iCalendar: number;
    clickables: Map<number, number>;
  }) {
    this.postMessageToChannel(
      LisioBroadcastChannelMessagesNames.BCMR_REFRESH_CALENDAR,
      datas,
    );
  }

  public sendRemovedNodesMessage(
    toRemove: {
      content: string;
      position: number;
      alreadyExists: boolean;
    }[],
  ) {
    this.postMessageToChannel(
      LisioBroadcastChannelMessagesNames.BCMR_REMOVE_NODES,
      {
        toRemove,
      },
    );
  }

  public sendScrollToMessage(target: string) {
    this.postMessageToChannel(
      LisioBroadcastChannelMessagesNames.BCMS_SCROLL_TO,
      { target },
    );
  }

  private postMessageToChannel(
    name: LisioBroadcastChannelMessagesNames,
    datas: object,
  ) {
    if (__BUILD_TARGET__ === "extension") {
      if (name.startsWith("bcms")) {
        const bcmrChannel = this._channels.get("broadcast-channel-reading");
        bcmrChannel?.postMessage({
          name: "redirect_to_site",
          datas: JSON.stringify({ name, datas }),
        });
      } else if (name.startsWith("bcmr")) {
        const bcmsChannel = this._channels.get("broadcast-channel-site");
        bcmsChannel?.postMessage({
          name: "redirect_to_reading",
          datas: JSON.stringify({ name, datas }),
        });
      }
    } else {
      this._broadcastChannel.postMessage({
        name,
        datas: JSON.stringify(datas),
      });
    }
  }
}

export { LisioBroadcastChannelController };
