import wsAPI from "@/app/[language]/site/lib/wsAPI";
import {
  AbilitySiteYell,
  Avatar,
  SiteYellItem,
} from "@/app/[language]/site/type";
import {
  formatText,
  getAbilityUpText,
  getDefaultAvatarID,
  getGenreText,
  getSiteName,
} from "@/utilities/Utility";
import { wwwAPIPath } from "@/utilities/wwwAPI";
import CryptoJS from "crypto-js";
import { makeAutoObservable } from "mobx";
import { useTranslations } from "next-intl";
import { RefObject } from "react";
import { toast } from "react-toastify";

const EventPB = require("@/Event_pb");

export class SiteView {
  siteID = "";
  isEditable = false;
  isNew = false;
  wasNotify = false;
  siteNotify = "";
  siteName = "";
  isNetMode = false;
  avatars = [] as Avatar[];
  siteHand = "";
  situationValue = 0;
  siteYellItems = [] as SiteYellItem[];
  lastPendingSiteYell = undefined as SiteYellItem | undefined;
  isPendingSiteYellOpened = false;
  isSiteYellsViewLowest = false;
  isAvatarsViewOpened = false;
  isSiteHand = false;
  input = "";
  siteYellsView = undefined as RefObject<HTMLDivElement> | undefined;

  constructor(
    siteID: string,
    siteNotify: string,
    isEditable: boolean,
    isNetMode: boolean,
    siteYellItems: SiteYellItem[],
  ) {
    this.siteID = siteID;
    this.siteNotify = siteNotify;
    this.isEditable = isEditable;
    this.isNetMode = isNetMode;
    this.siteYellItems = siteYellItems;

    makeAutoObservable(this);
  }

  setSiteYell(targetSiteYellID: number, siteYell: string) {
    const targetSiteYell = this.siteYellItems.find(
      ({ siteYellID }) => siteYellID === targetSiteYellID,
    );
    if (targetSiteYell) {
      targetSiteYell.siteYell = siteYell;
    }
  }

  setSiteYellsView = (siteYellsView: RefObject<HTMLDivElement>) => {
    this.siteYellsView = siteYellsView;
  };

  putSiteYell(siteYellItem: SiteYellItem, isGetSiteYell: boolean) {
    if (isGetSiteYell) {
      this.siteYellItems.unshift(siteYellItem);
    } else {
      this.siteYellItems.push(siteYellItem);
    }
  }

  siteYellsViewMove() {
    setTimeout(() => {
      if (this.siteYellsView) {
        const { current } = this.siteYellsView;
        if (current) {
          current.scrollTop = current.scrollHeight;
        }
      }
    }, 0);
  }

  setLastPendingSiteYell = (siteYellItem: SiteYellItem) => {
    this.lastPendingSiteYell = siteYellItem;
    this.isPendingSiteYellOpened = true;
  };

  setNew = (isNew: boolean) => {
    this.isNew = isNew;
  };

  setSiteNotify = (siteNotify: string) => {
    this.siteNotify = siteNotify;
  };

  setPendingSiteYellOpened = (isPendingSiteYellOpened: boolean) => {
    this.isPendingSiteYellOpened = isPendingSiteYellOpened;
  };

  setAvatarsViewOpened = (isAvatarsViewOpened: boolean) => {
    this.isAvatarsViewOpened = isAvatarsViewOpened;
  };

  doCallSiteAvatar = (
    siteName: string,
    siteHand: string,
    situationValue: number,
    avatars: Avatar[],
  ) => {
    this.siteName = siteName;
    this.siteHand = siteHand;
    this.situationValue = situationValue;
    this.avatars = avatars;
  };

  setSiteYellsViewLowest = (isSiteYellsViewLowest: boolean) => {
    this.isSiteYellsViewLowest = isSiteYellsViewLowest;
  };

  wipeSiteYell = (siteYellID: number) => {
    const i = this.siteYellItems.findIndex(
      (siteYellItem) => siteYellItem.siteYellID === siteYellID,
    );
    const siteYellItem = this.siteYellItems[i];
    this.siteYellItems.splice(i, 1, {
      siteYellID,
      avatarID: siteYellItem.avatarID,
      avatarName: siteYellItem.avatarName,
      siteYellVariety: "@Wiped",
      date: siteYellItem.date,
    });
  };

  setInput = (input: string) => {
    this.input = input;
  };
}

const getSiteYell = ({
  avatarID,
  avatarName,
  siteYell,
  date,
  siteYellID,
}: {
  avatarID: string;
  avatarName: string;
  siteYell: string;
  date: number;
  siteYellID: number;
}): SiteYellItem => {
  const dateText = new Date(date).toLocaleTimeString();
  switch (avatarName) {
    case "@Enter":
      return {
        siteYellID,
        avatarID,
        avatarName: siteYell,
        siteYellVariety: avatarName,
        date: dateText,
      };
    case "@Quit":
      return {
        siteYellID,
        avatarID,
        avatarName: siteYell,
        siteYellVariety: avatarName,
        date: dateText,
      };
    case "@Site":
      return {
        siteYellID,
        avatarID,
        avatarName: siteYell,
        siteYellVariety: avatarName,
        date: dateText,
      };
    case "@Net":
      return {
        siteYellID,
        avatarID,
        avatarName: siteYell,
        siteYellVariety: avatarName,
        date: dateText,
      };
    case "@Notify":
      return {
        siteYellID,
        avatarID,
        siteYellVariety: avatarName,
        date: dateText,
        siteYell,
      };
    case "@Wiped":
      return {
        siteYellID,
        avatarID,
        avatarName: siteYell,
        siteYellVariety: avatarName,
        date: dateText,
      };
    case "@Invite":
    case "@Comment":
    case "@Ability":
    case "@Level":
    case "@TV":
      return {
        siteYellID,
        avatarID,
        siteYellVariety: avatarName,
        date: dateText,
        siteYell: JSON.parse(siteYell),
      };
    case "":
      return {
        siteYellID,
        siteYellVariety: "",
        siteYell,
      };
    default:
      return {
        siteYellID,
        siteYellVariety: null,
        date: dateText,
        avatarID,
        avatarName,
        siteYell,
      };
  }
};

export default function setSiteStore(
  t: ReturnType<typeof useTranslations<string>>,
) {
  return {
    titleView: undefined as RefObject<HTMLDivElement> | undefined,
    isSiteWindowOpened: false,
    isSiteCipherWindowOpened: false,
    isNewSiteWindowOpened: false,
    isConfigureOpened: false,
    targetSiteID: "",
    saveTraffic:
      typeof window === "object" &&
      window.localStorage.getItem("saveTraffic") === "true",
    autoEnterNotify:
      typeof window === "object" &&
      window.localStorage.getItem("autoEnterNotify") !== "false",
    autoEnterDefault:
      typeof window === "object" &&
      window.localStorage.getItem("autoEnterDefault") !== "false",
    autoEnterPlatform:
      typeof window === "object" &&
      window.localStorage.getItem("autoEnterPlatform") !== "false",
    siteViews: [] as SiteView[],
    siteAvatarID: "",
    siteAvatarName: "",
    isLoggedIn: false,
    isVisible: false,
    isLoading: true,

    setTitleView(titleView: RefObject<HTMLDivElement>) {
      this.titleView = titleView;
    },

    setEventHandler() {
      const autoEnter = () => {
        wsAPI.send({
          eventID: EventPB.Event.EventID.ENTER_SITE,
          text: JSON.stringify({
            siteID: "00000000-0000-0000-0000-000000000003",
            siteCipher: "",
          }),
        });
        if (this.autoEnterNotify) {
          wsAPI.send({
            eventID: EventPB.Event.EventID.ENTER_SITE,
            text: JSON.stringify({
              siteID: "00000000-0000-0000-0000-000000000000",
              siteCipher: "",
            }),
          });
        }
        if (this.autoEnterDefault) {
          wsAPI.send({
            eventID: EventPB.Event.EventID.ENTER_SITE,
            text: JSON.stringify({
              siteID: "00000000-0000-0000-0000-000000000001",
              siteCipher: "",
            }),
          });
        }
        if (this.autoEnterPlatform) {
          wsAPI.send({
            eventID: EventPB.Event.EventID.ENTER_SITE,
            text: JSON.stringify({
              siteID: "00000000-0000-0000-0000-000000000002",
              siteCipher: "",
            }),
          });
        }
      };

      const autoLogIn = () => {
        if (window.localStorage.getItem("autoLogIn") === "true") {
          wsAPI.send({
            eventID: EventPB.Event.EventID.LOG_IN,
            text: JSON.stringify({
              avatarID: window.localStorage.getItem("avatarID") ?? "",
              avatarCipher: CryptoJS.AES.decrypt(
                window.localStorage.getItem("avatarCipher") ?? "",
                "591A6F91-2A27-4A88-88FA-0FEB7CB5FD94",
              ).toString(CryptoJS.enc.Utf8),
            }),
          });
        }
      };

      const doNotify = (avatarID: string, toNotify: string, siteID: string) => {
        if (
          !["denied", "default"].includes(Notification?.permission) &&
          !this.isVisible
        ) {
          const siteView = this.getSiteView(siteID);
          if (siteView?.wasNotify === false) {
            new Notification("Qwilight", {
              body: toNotify,
              icon: `${wwwAPIPath}/drawing?avatarID=${getDefaultAvatarID(
                avatarID,
              )}&drawingVariety=0`,
            });
            siteView.wasNotify = true;
          }
        }
      };

      wsAPI.setEventDefaultHandler(({ eventID, text }) => {
        switch (eventID) {
          case EventPB.Event.EventID.ESTABLISH:
            if (text) {
              const { avatarID, avatarName } = JSON.parse(text);
              this.setSiteAvatar(avatarID, avatarName);
              this.setLoading(false);
              autoEnter();
              const totem = window.sessionStorage.getItem("totem");
              if (totem) {
                wsAPI.send({
                  eventID: EventPB.Event.EventID.LOG_IN,
                  text: totem,
                });
              } else {
                autoLogIn();
              }
            }
            break;
          case EventPB.Event.EventID.FAILED_VALIDATE_TOTEM:
            autoLogIn();
            break;
          // EventPB.Event.EventID.LOG_IN
          case undefined:
            if (text) {
              const { totem, avatarID, avatarName } = JSON.parse(text);
              if (totem !== window.sessionStorage.getItem("totem")) {
                toast.success(t("loggedInText", { avatarName }));
              }
              window.sessionStorage.setItem("totem", totem);
              this.setSiteAvatar(avatarID, avatarName);
              this.setLoggedIn(true);
              autoEnter();
            }
            break;
          case EventPB.Event.EventID.NOT_LOG_IN:
            if (text) {
              toast.success(
                t("notLoggedInText", {
                  avatarName: this.siteAvatarName,
                }),
              );
              window.sessionStorage.removeItem("totem");
              const { avatarID, avatarName } = JSON.parse(text);
              this.setSiteAvatar(avatarID, avatarName);
              this.setLoggedIn(false);
              autoEnter();
            }
            break;
          case EventPB.Event.EventID.SITE_YELL:
            if (text) {
              const {
                siteID,
                avatarID,
                avatarName,
                siteYell,
                siteYellID,
                date,
              } = JSON.parse(text);
              const siteYellItem = getSiteYell({
                avatarID,
                avatarName,
                siteYell,
                date,
                siteYellID,
              });
              const siteView = this.getSiteView(siteID);
              if (siteView) {
                siteView.putSiteYell(siteYellItem, false);
                if (this.targetSiteID === siteID) {
                  if (
                    avatarID === this.siteAvatarID ||
                    siteView.isSiteYellsViewLowest
                  ) {
                    siteView.siteYellsViewMove();
                  } else {
                    siteView.setLastPendingSiteYell(siteYellItem);
                  }
                } else {
                  siteView.setNew(true);
                }
                const ltDate = new Date(Number(date)).toLocaleTimeString();
                let toNotify = "";
                switch (avatarName) {
                  case "@Notify":
                    siteView.setSiteNotify(siteYell);
                    toNotify = `${t("siteYellTaehui")} (${ltDate}) ${siteYell}`;
                    break;
                  case "@Comment": {
                    const {
                      avatarName,
                      artist,
                      title,
                      genre,
                      levelText,
                      stand,
                    } = JSON.parse(siteYell);
                    toNotify = `${avatarName} (${ltDate}) ${levelText} ${artist} - ${title} ${getGenreText(
                      genre,
                    )} ${t("textStand", { value: formatText(stand) })}`;
                    break;
                  }
                  case "@Ability": {
                    toNotify = getAbilityUpText(
                      JSON.parse(siteYell) as AbilitySiteYell,
                    );
                    break;
                  }
                  case "@Level": {
                    const { title } = JSON.parse(siteYell);
                    toNotify = t("wwwLevelClearText", { title });
                    break;
                  }
                  case "@Enter": {
                    toNotify = `${siteYell} ${ltDate} ${t("siteYellEnter")}`;
                    break;
                  }
                  case "@Quit": {
                    toNotify = `${siteYell} ${ltDate} ${t("siteYellQuit")}`;
                    break;
                  }
                  case "@Invite": {
                    const { avatarName, siteName } = JSON.parse(siteYell);
                    toNotify = `${avatarName} (${ltDate}) ${t(
                      "siteYellInvite",
                      {
                        siteName,
                      },
                    )}`;
                    break;
                  }
                  case "@TV": {
                    const { title, text } = JSON.parse(siteYell);
                    toNotify = `${text} (${ltDate}) ${t("siteYellTV", {
                      title,
                    })}`;
                    break;
                  }
                  case "@Wiped":
                    toNotify = `${avatarName} (${ltDate}) ${t("wipedSiteYell")}`;
                    break;
                  case "":
                    toNotify = siteYell;
                    break;
                  case null:
                    toNotify = `${avatarName} (${ltDate}) ${siteYell}`;
                    break;
                }
                if (toNotify) {
                  doNotify(avatarID, toNotify, siteID);
                }
              }
            }
            break;
          case EventPB.Event.EventID.MODIFY_SITE_YELL:
            if (text) {
              const { siteID, siteYell, siteYellID } = JSON.parse(text);
              this.getSiteView(siteID)?.setSiteYell(siteYellID, siteYell);
            }
            break;
          case EventPB.Event.EventID.WIPE_SITE_YELL:
            if (text) {
              const { siteID, siteYellID } = JSON.parse(text);
              this.getSiteView(siteID)?.wipeSiteYell(siteYellID);
            }
            break;
          case EventPB.Event.EventID.GET_SITE_YELLS:
            if (text) {
              const { siteID, data } = JSON.parse(text);
              const siteView = this.getSiteView(siteID);
              if (siteView) {
                if (siteView.siteYellsView) {
                  const { current } = siteView.siteYellsView;
                  if (current) {
                    const lastPosition1BeforeCalled =
                      current.scrollHeight - current.clientHeight;

                    data
                      .reverse()
                      .map(getSiteYell)
                      .forEach((siteYellItem: SiteYellItem) => {
                        siteView.putSiteYell(siteYellItem, true);
                      });

                    setTimeout(() => {
                      current.scrollTop =
                        current.scrollHeight -
                        current.clientHeight -
                        lastPosition1BeforeCalled;
                    }, 0);
                  }
                }
              }
            }
            break;
          case EventPB.Event.EventID.CALL_SITE_AVATAR:
            if (text) {
              const { siteID, siteName, situationValue, data, siteHand } =
                JSON.parse(text);
              const siteView = this.getSiteView(siteID);
              if (siteView) {
                const avatars = data.map(
                  ({
                    avatarID,
                    avatarName,
                    avatarConfigure,
                    isValve,
                    isAudioInput,
                  }: {
                    avatarID: string;
                    avatarName: string;
                    avatarConfigure: number;
                    isValve: boolean;
                    isAudioInput: boolean;
                  }) => ({
                    avatarID,
                    avatarName,
                    avatarConfigure,
                    isSiteHand: avatarID === siteHand,
                    isMe: this.siteAvatarID === avatarID,
                    isValve,
                    isAudioInput,
                  }),
                );
                siteView.doCallSiteAvatar(
                  getSiteName(siteName, t),
                  siteHand,
                  situationValue,
                  avatars,
                );
              }
            }
            break;
          case EventPB.Event.EventID.ENTER_SITE:
            if (text) {
              const { siteID, siteNotify, isEditable, isNetMode, data } =
                JSON.parse(text);
              const siteView = new SiteView(
                siteID,
                siteNotify,
                isEditable,
                isNetMode,
                data.map(getSiteYell),
              );
              this.putSiteView(siteView);
              this.setTargetSiteID(siteID);
            }
            break;
          case EventPB.Event.EventID.QUIT_SITE:
            if (text) {
              this.wipeSiteView(text);
            }
            break;
          case EventPB.Event.EventID.NOTIFY:
            if (text) {
              const { v, text: toNotify } = JSON.parse(text);
              switch (v) {
                case 0:
                  toast.success(toNotify);
                  break;
                case 1:
                  toast.error(toNotify);
                  break;
                case 2:
                  toast.warning(toNotify);
                  break;
                case 3:
                  toast.info(toNotify);
                  break;
              }
            }
            break;
          case EventPB.Event.EventID.POST_FILE:
            if (text) {
              this.getSiteView(this.targetSiteID)?.setInput(text);
            }
            break;
        }
      });
    },

    setEventCloseHandler() {
      wsAPI.setEventCloseHandler(() => {
        this.setSiteAvatar("", "");
        this.setLoggedIn(false);
        this.wipeSiteViews();
        this.setLoading(true);
      });
    },

    setLoading(isLoading: boolean) {
      this.isLoading = isLoading;
    },

    setSiteAvatar(siteAvatarID: string, siteAvatarName: string) {
      this.siteAvatarID = siteAvatarID;
      this.siteAvatarName = siteAvatarName;
      wsAPI.setAvatarID(this.siteAvatarID);
    },

    setSaveTraffic(SaveTraffic: boolean) {
      this.saveTraffic = SaveTraffic;
    },

    setAutoEnterNotify(autoEnterNotify: boolean) {
      this.autoEnterNotify = autoEnterNotify;
    },

    setAutoEnterDefault(autoEnterDefault: boolean) {
      this.autoEnterDefault = autoEnterDefault;
    },

    setAutoEnterPlatform(autoEnterPlatform: boolean) {
      this.autoEnterPlatform = autoEnterPlatform;
    },

    setSiteWindowOpened(isSiteWindowOpened: boolean) {
      this.isSiteWindowOpened = isSiteWindowOpened;
    },

    setSiteCipherWindowOpened(isSiteCipherWindowOpened: boolean) {
      this.isSiteCipherWindowOpened = isSiteCipherWindowOpened;
    },

    setNewSiteWindowOpened(isNewSiteWindowOpened: boolean) {
      this.isNewSiteWindowOpened = isNewSiteWindowOpened;
    },

    setConfigureOpened(isConfigureOpened: boolean) {
      this.isConfigureOpened = isConfigureOpened;
    },

    getSiteView(targetSiteID: string) {
      return this.siteViews.find(({ siteID }) => siteID === targetSiteID);
    },

    setTargetSiteID(targetSiteID: string) {
      this.targetSiteID = targetSiteID;
      const siteView = this.getSiteView(this.targetSiteID);
      if (siteView) {
        siteView.setNew(false);
      }
    },

    setVisible() {
      return (this.isVisible = document.visibilityState === "visible");
    },

    setLoggedIn(isLoggedIn: boolean) {
      this.isLoggedIn = isLoggedIn;
    },

    putSiteView(siteView: SiteView) {
      this.siteViews.push(siteView);
    },

    wipeSiteView(targetSiteID: string) {
      this.siteViews.splice(
        this.siteViews.findIndex(({ siteID }) => siteID === targetSiteID),
        1,
      );
      this.targetSiteID = this.siteViews[this.siteViews.length - 1].siteID;
    },

    wipeSiteViews() {
      this.siteViews = [];
      this.targetSiteID = "";
    },
  };
}
