import { MutableRefObject } from "react";
import { toast } from "react-toastify";
import { runInAction } from "mobx";
import { sprintf } from "sprintf-js";
import CryptoJS from "crypto-js";
import { TFunction } from "i18next";

import SiteComponent from "src/site/SiteComponent";
import { AbilitySiteYell, Avatar, SiteView, SiteYell } from "src/site/Site";
import {
  formatText,
  getAbilityUpText,
  getDefaultAvatarID,
  getGenreText,
  getSiteName,
} from "src/Utility";
import { wwwAPI, wwwAXIOS } from "src/Www";
import { GetSitesAPI, SitesAPISite } from "src/wwwAPI";

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

const getSiteYellItem = ({
  avatarID,
  avatarName,
  siteYell,
  date,
  siteYellID,
}: {
  avatarID: string;
  avatarName: string;
  siteYell: string;
  date: number;
  siteYellID: number;
}): SiteYell | null => {
  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() {
  return {
    avatars: [] as Avatar[],
    siteYells: [] as SiteYell[],
    sites: [] as SitesAPISite[],
    isSignInOpened: false,
    isSiteWindowOpened: false,
    isSiteCipherWindowOpened: false,
    isNewSiteWindowOpened: false,
    isConfigureOpened: false,
    targetSiteID: "",
    siteNotify: "",
    saveTraffic: window.localStorage.getItem("saveTraffic") === "true",
    autoEnterNotify: window.localStorage.getItem("autoEnterNotify") !== "false",
    autoEnterDefault:
      window.localStorage.getItem("autoEnterDefault") !== "false",
    autoEnterPlatform:
      window.localStorage.getItem("autoEnterPlatform") !== "false",
    isEditable: true,
    input: "",
    siteViews: [] as SiteView[],
    siteAvatarID: "",
    siteAvatarName: "",
    isSignedIn: false,
    isSiteYellsViewLowest: true,
    isVisible: false,
    isLoading: true,
    lastPendingSiteYell: undefined as SiteYell | undefined,
    isPendingSiteYellOpened: false,
    isAvatarsOpened: false,

    setEventHandler(
      siteYellsView: MutableRefObject<HTMLDivElement | null>,
      t: TFunction,
    ) {
      const autoEnter = () => {
        if (this.autoEnterNotify) {
          SiteComponent.send({
            eventID: EventPB.Event.EventID.ENTER_SITE,
            text: JSON.stringify({
              siteID: "00000000-0000-0000-0000-000000000000",
              siteCipher: "",
            }),
          });
        }
        if (this.autoEnterDefault) {
          SiteComponent.send({
            eventID: EventPB.Event.EventID.ENTER_SITE,
            text: JSON.stringify({
              siteID: "00000000-0000-0000-0000-000000000001",
              siteCipher: "",
            }),
          });
        }
        if (this.autoEnterPlatform) {
          SiteComponent.send({
            eventID: EventPB.Event.EventID.ENTER_SITE,
            text: JSON.stringify({
              siteID: "00000000-0000-0000-0000-000000000002",
              siteCipher: "",
            }),
          });
        }
      };

      const autoSignIn = () => {
        if (window.localStorage.getItem("autoSignIn") === "true") {
          SiteComponent.send({
            eventID: EventPB.Event.EventID.SIGN_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: `${wwwAPI}/drawing?avatarID=${encodeURIComponent(
                getDefaultAvatarID(avatarID),
              )}&drawingVariety=0`,
            });
            siteView.wasNotify = true;
          }
        }
      };

      SiteComponent.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) {
                SiteComponent.send({
                  eventID: EventPB.Event.EventID.SIGN_IN,
                  text: totem,
                });
              } else {
                autoSignIn();
              }
            }
            break;
          case EventPB.Event.EventID.FAILED_VALIDATE_TOTEM:
            autoSignIn();
            break;
          // EventPB.Event.EventID.SIGN_IN
          case undefined:
            if (text) {
              this.setSignInOpened(false);
              const { totem, avatarID, avatarName } = JSON.parse(text);
              if (totem !== window.sessionStorage.getItem("totem")) {
                toast.success(sprintf(t("signedInText"), avatarName));
              }
              window.sessionStorage.setItem("totem", totem);
              this.setSiteAvatar(avatarID, avatarName);
              this.setSignedIn(true);
              autoEnter();
            }
            break;
          case EventPB.Event.EventID.NOT_SIGN_IN:
            if (text) {
              toast.success(sprintf(t("notSignedInText"), this.siteAvatarName));
              window.sessionStorage.removeItem("totem");
              const { avatarID, avatarName } = JSON.parse(text);
              this.setSiteAvatar(avatarID, avatarName);
              this.setSignedIn(false);
              autoEnter();
            }
            break;
          case EventPB.Event.EventID.SITE_YELL:
            if (text) {
              const {
                siteID,
                avatarID,
                avatarName,
                siteYell,
                siteYellID,
                date,
              } = JSON.parse(text);
              const siteYellItem = getSiteYellItem({
                avatarID,
                avatarName,
                siteYell,
                date,
                siteYellID,
              });
              if (siteYellItem) {
                this.putSiteYell(siteID, siteYellItem, false);
                if (this.targetSiteID === siteID) {
                  this.putTargetSiteYell(siteYellItem, false);
                  if (
                    avatarID === this.siteAvatarID ||
                    this.isSiteYellsViewLowest
                  ) {
                    this.siteYellsViewMove(siteYellsView);
                  } else {
                    this.lastPendingSiteYell = siteYellItem;
                    this.setPendingSiteYellOpened(true);
                  }
                } else {
                  this.setModifiedSiteViews(siteID, (siteView) => {
                    siteView.isNew = true;
                  });
                }
              }
              const ltDate = new Date(Number(date)).toLocaleTimeString();
              let toNotify = "";
              switch (avatarName) {
                case "@Notify":
                  if (this.targetSiteID === siteID) {
                    this.siteNotify = siteYell;
                  } else {
                    const siteView = this.getSiteView(siteID);
                    if (siteView) {
                      siteView.siteNotify = 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,
                  )} ${sprintf(t("textStand"), formatText(stand))}`;
                  break;
                }
                case "@Ability": {
                  toNotify = getAbilityUpText(
                    t,
                    JSON.parse(siteYell) as AbilitySiteYell,
                  );
                  break;
                }
                case "@Level": {
                  const { title } = JSON.parse(siteYell);
                  toNotify = sprintf(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}) ${sprintf(
                    t("siteYellInvite"),
                    siteName,
                  )}`;
                  break;
                }
                case "@TV": {
                  const { title, text } = JSON.parse(siteYell);
                  toNotify = `${text} (${ltDate}) ${sprintf(
                    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.doModifySiteYell(siteID, siteYell, siteYellID);
              if (this.targetSiteID === siteID) {
                this.doModifyTargetSiteYell(siteYell, siteYellID);
              }
            }
            break;
          case EventPB.Event.EventID.ERASE_SITE_YELL:
            if (text) {
              const { siteID, siteYellID } = JSON.parse(text);
              this.wipeSiteYell(siteID, siteYellID);
              if (this.targetSiteID === siteID) {
                this.wipeTargetSiteYell(siteYellID);
              }
            }
            break;
          case EventPB.Event.EventID.GET_SITE_YELLS:
            if (text) {
              const { current } = siteYellsView;
              if (current) {
                const lastPosition1BeforeCalled =
                  current.scrollHeight - current.clientHeight;
                const { siteID, data } = JSON.parse(text);

                data
                  .reverse()
                  .map(getSiteYellItem)
                  .forEach((siteYellItem: SiteYell) => {
                    this.putSiteYell(siteID, siteYellItem, true);
                    if (this.targetSiteID === siteID) {
                      this.putTargetSiteYell(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 avatars = data.map(
                ({
                  avatarID,
                  avatarName,
                  avatarConfigure,
                  isValve,
                  isAudioInput,
                }: {
                  avatarID: string;
                  avatarName: string;
                  avatarConfigure: number;
                  isValve: boolean;
                  isAudioInput: boolean;
                }) => {
                  return {
                    avatarID,
                    avatarName,
                    avatarConfigure,
                    isSiteHand: avatarID === siteHand,
                    isMe: this.siteAvatarID === avatarID,
                    isValve,
                    isAudioInput,
                  };
                },
              );
              this.setModifiedSiteViews(siteID, (siteView) => {
                siteView.siteName = getSiteName(siteName, t);
                siteView.avatars = avatars;
                siteView.siteHand = siteHand;
                siteView.situationValue = situationValue;
              });
              if (siteID === this.targetSiteID) {
                this.setAvatars(avatars);
              }
            }
            break;
          case EventPB.Event.EventID.ENTER_SITE:
            if (text) {
              const { siteID, siteNotify, isEditable, isNetMode, data } =
                JSON.parse(text);
              this.putSiteView({
                siteID,
                isEditable,
                isNew: false,
                wasNotify: false,
                siteNotify,
                siteName: "",
                siteHand: "",
                situationValue: 0,
                isNetMode,
                avatars: [],
                siteYells: data.map(getSiteYellItem),
              });
              this.onSiteIDModified(siteID, siteYellsView);
            }
            break;
          case EventPB.Event.EventID.QUIT_SITE:
            if (text) {
              this.quitSiteView(text);
              this.onSiteIDModified(
                this.siteViews[this.siteViews.length - 1]?.siteID,
                siteYellsView,
              );
            }
            break;
          case EventPB.Event.EventID.WARNING:
            toast.warning(text);
            break;
          case EventPB.Event.EventID.NOTIFY_YES:
            toast.success(text);
            break;
          case EventPB.Event.EventID.NOTIFY_INFO:
            toast.info(text);
            break;
          case EventPB.Event.EventID.POST_FILE:
            if (text) {
              this.setInput(text);
            }
            break;
        }
      });
    },

    setEventCloseHandler(
      siteYellsView: MutableRefObject<HTMLDivElement | null>,
    ) {
      SiteComponent.setEventCloseHandler(() => {
        this.setSiteAvatar("", "");
        this.setSignedIn(false);
        this.onSiteIDModified("", siteYellsView);
        this.wipeSiteViews();
        this.setLoading(true);
      });
    },

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

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

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

    putSiteYell(siteID: string, siteYell: SiteYell, isGetSiteYell: boolean) {
      const siteView = this.getSiteView(siteID);
      if (isGetSiteYell) {
        siteView?.siteYells?.unshift(siteYell);
      } else {
        siteView?.siteYells?.push(siteYell);
      }
    },

    putTargetSiteYell(siteYell: SiteYell, isGetSiteYell: boolean) {
      if (isGetSiteYell) {
        this.siteYells.unshift(siteYell);
      } else {
        this.siteYells.push(siteYell);
      }
    },

    doModifySiteYell(siteID: string, siteYell: string, siteYellID: number) {
      const targetSiteYell = this.getSiteView(siteID)?.siteYells?.find(
        (siteYell) => siteYell.siteYellID === siteYellID,
      );
      if (targetSiteYell) {
        targetSiteYell.siteYell = siteYell;
      }
    },

    doModifyTargetSiteYell(siteYell: string, siteYellID: number) {
      const targetSiteYell = this.siteYells?.find(
        (siteYell) => siteYell.siteYellID === siteYellID,
      );
      if (targetSiteYell) {
        targetSiteYell.siteYell = siteYell;
      }
    },

    wipeSiteYell(siteID: string, siteYellID: number) {
      const siteYells = this.getSiteView(siteID)?.siteYells;
      if (siteYells) {
        const i = siteYells.findIndex(
          (siteYell) => siteYell.siteYellID === siteYellID,
        );
        const siteYell = siteYells[i];
        siteYells.splice(i, 1, {
          siteYellID,
          avatarID: siteYell.avatarID,
          avatarName: siteYell.avatarName,
          siteYellVariety: "@Wiped",
          date: siteYell.date,
        });
      }
    },

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

    setModifiedSiteViews(
      targetSiteID: string,
      onModified: (siteView: SiteView) => void,
    ) {
      const tmpSiteView = this.siteViews.find(
        ({ siteID }) => targetSiteID === siteID,
      );
      if (tmpSiteView) {
        onModified(tmpSiteView);
      }
    },

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

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

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

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

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

    async getSites(t: TFunction) {
      const { status, data } = await wwwAXIOS.get<GetSitesAPI>("/sites");
      if (status === 200) {
        runInAction(() => {
          this.sites = data.map(
            ({ siteID, siteName, siteConfigure, hasCipher, avatarCount }) => {
              return {
                siteID,
                siteName: getSiteName(siteName, t),
                siteConfigure,
                hasCipher,
                avatarCount,
              };
            },
          );
          this.setSiteWindowOpened(true);
        });
      }
    },

    setSignInOpened(isSignInOpened: boolean) {
      this.isSignInOpened = isSignInOpened;
    },

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

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

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

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

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

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

    onSiteIDModified(
      siteID: string,
      siteYellsView: MutableRefObject<HTMLDivElement | null>,
    ) {
      this.targetSiteID = siteID;
      const siteView = this.getSiteView(siteID);
      if (siteView) {
        if (this.isVisible) {
          siteView.wasNotify = false;
        }
        this.setModifiedSiteViews(siteID, (siteView) => {
          siteView.isNew = false;
        });
        this.isEditable = siteView.isEditable;
        this.siteNotify = siteView.siteNotify;
        this.avatars = [...siteView.avatars];
        this.siteYells = [...siteView.siteYells];
        this.siteYellsViewMove(siteYellsView);
      } else {
        this.isEditable = false;
        this.siteNotify = "";
        this.avatars = [];
        this.siteYells = [];
      }
    },

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

    siteYellsViewMove(siteYellsView: MutableRefObject<HTMLDivElement | null>) {
      setTimeout(() => {
        const { current } = siteYellsView;
        if (current) {
          current.scrollTop = current.scrollHeight;
        }
      }, 0);
    },

    setAvatarsOpened(isAvatarsOpened: boolean) {
      this.isAvatarsOpened = isAvatarsOpened;
    },

    setSignedIn(isSignedIn: boolean) {
      this.isSignedIn = isSignedIn;
    },

    setAvatars(avatars: Avatar[]) {
      this.avatars = avatars;
    },

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

    quitSiteView(siteID: string) {
      this.siteViews.splice(
        this.siteViews.findIndex((siteView) => siteView.siteID === siteID),
        1,
      );
    },

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