import { compress, decompress } from "wasm-gzip";
import { deepClone } from "web-utils";
import * as Y from "yjs";
import { clientStartSync } from "../../../utils/sync/ws-client";
import { w } from "../../../utils/types/general";
import { Loading } from "../../../utils/ui/loading";
import { EmptySite, PG } from "./ed-global";
import { treeRebuild } from "./tree/build";
import { evalCJS } from "../../view/logic/load-code-new";
import { reloadPage } from "./ed-route";
import { loadSite } from "./ed-site";

const decoder = new TextDecoder();

export const edInitSync = (p: PG) => {
  const session = JSON.parse(
    localStorage.getItem("prasi-session") || "null"
  ) as { data: { user: { id: string; username: string } } };
  if (!session) {
    navigate("/login");
    return <Loading note="logging in" />;
  }
  p.user.id = session.data.user.id;
  p.user.username = session.data.user.username;

  if (p.sync) {
    if (p.site.id === "--loading--") return false;
    if (params.site_id !== p.site.id) {
      p.site = deepClone(EmptySite);
      p.site.id = "--loading--";
      p.ui.popup.code.init = false;
      p.sync.site.load(params.site_id).then(async (site) => {
        if (site) {
          await loadSite(p, site);
          p.render();
        } else {
          alert("Site not found. redirecting...");
          location.href = `/ed/`;
        }
      });
      return false;
    }

    if (!params.page_id && params.site_id) {
      db.page
        .findFirst({
          where: {
            is_deleted: false,
            is_default_layout: false,
            id_site: params.site_id,
          },
          select: { id: true },
        })
        .then((e) => {
          if (e) navigate(`/ed/${params.site_id}/${e.id}`);
        });
      return false;
    }
  }
  if (!p.sync) {
    clientStartSync({
      user_id: session.data.user.id,
      site_id: params.site_id,
      page_id: params.page_id,
      events: {
        code(arg) {
          p.ui.popup.code.error = false;

          if (arg.event === "code-loading") {
            p.ui.popup.code.log = "";
            p.ui.popup.code.loading = true;
            p.render();
          } else if (arg.event === "code-done") {
            if (typeof arg.content === "string") {
              if (arg.content !== "OK") {
                p.ui.popup.code.error = true;
              }

              p.ui.popup.code.log += arg.content;
            }
            p.ui.popup.code.loading = false;

            if (arg.src) {
              const w = window as any;
              const module = evalCJS(decoder.decode(decompress(arg.src)));
              if (typeof module === "object") {
                for (const [k, v] of Object.entries(module)) {
                  w[k] = v;
                }
              }
            }
            p.render();
          } else {
            if (typeof arg.content === "string")
              p.ui.popup.code.log += arg.content;
            p.render();
          }
        },
        activity(arg) {},
        opened() {
          if (w.offline) {
            console.log("reconnected!");
            w.offline = false;
            p.ui.syncing = true;
            p.sync.activity("site", { type: "join", id: params.site_id });
            p.render();
          } else {
            w.offline = false;
            p.render();
          }
        },
        shakehand(client_id) {
          p.user.client_id = client_id;
        },
        disconnected() {
          console.log("offline, reconnecting...");
          w.offline = true;
          p.render();
          return {
            reconnect: true,
          };
        },
        async editor_start(e) {
          if (p.ui.syncing) {
            await reloadPage(p, params.page_id, "editor-start");
            if (p.page.doc) {
              p.page.doc.transact(() => {
                p.page.doc?.getMap("map").set("ts", Date.now());
              }, `sync`);
            }
          }

          if (params.site_id !== e.site_id || params.page_id !== e.page_id) {
            p.site.id = e.site_id;
            p.page.cur.id = e.page_id;
            navigate(`/ed/${e.site_id}/${e.page_id}`);
          } else {
            p.site.id = e.site_id;
            p.page.cur.id = e.page_id;
            p.render();
          }
        },
        site_updated(site) {
          for (const [k, v] of Object.entries(site)) {
            if (k === "js" || k === "js_compiled") {
              p.site[k] = decoder.decode(decompress(v as any));
            } else {
              (p.site as any)[k] = v;
            }
          }
          p.render();
        },
        async remote_svlocal(data) {
          let doc = null as any;
          if (data.type === "page" && p.page.cur.id === data.id) {
            doc = p.page.doc as Y.Doc;
          } else if (data.type === "comp" && p.comp.list[data.id]) {
            doc = p.comp.list[data.id].doc;
          }

          if (doc) {
            const diff_remote = Y.encodeStateAsUpdate(
              doc,
              decompress(data.sv_local)
            );
            const sv_remote = Y.encodeStateVector(doc);

            const sv = Buffer.from(compress(sv_remote));
            const diff = Buffer.from(compress(diff_remote));
            const res = await p.sync.yjs.sv_remote(
              data.type,
              data.id,
              sv,
              diff
            );
            if (res) {
              Y.applyUpdate(doc, decompress(res.diff), "sv_remote");
              await treeRebuild(p, { note: "sv_remote" });
            }
          }
        },
      },
    }).then((e) => {
      p.sync = e;
    });

    return false;
  }
  return true;
};
