import {
  ipcRenderer,
  contextBridge,
  webUtils,
  type IpcRendererEvent,
} from "electron";

type StoreFilter = "spark" | "apm" | "both";

type UpdateCenterSnapshot = {
  items: Array<{
    taskKey: string;
    packageName: string;
    displayName: string;
    currentVersion: string;
    newVersion: string;
    source: "aptss" | "apm";
    localIcon?: string;
    remoteIcon?: string;
    ignored?: boolean;
  }>;
  tasks: Array<{
    taskKey: string;
    packageName: string;
    source: "aptss" | "apm";
    localIcon?: string;
    remoteIcon?: string;
    status:
      | "queued"
      | "downloading"
      | "installing"
      | "completed"
      | "failed"
      | "cancelled";
    progress: number;
    logs: Array<{ time: number; message: string }>;
    errorMessage: string;
  }>;
  warnings: string[];
  hasRunningTasks: boolean;
};

type IpcRendererFacade = {
  on: typeof ipcRenderer.on;
  off: typeof ipcRenderer.off;
  send: typeof ipcRenderer.send;
  invoke: typeof ipcRenderer.invoke;
};

type WindowControlBridge = {
  minimize: () => void;
  toggleMaximize: () => void;
  close: () => void;
};

type UpdateCenterStateListener = (snapshot: UpdateCenterSnapshot) => void;
type UpdateCenterStartTask = {
  taskKey: string;
  id: number;
};

const updateCenterStateListeners = new Map<
  UpdateCenterStateListener,
  (_event: IpcRendererEvent, snapshot: UpdateCenterSnapshot) => void
>();

// --------- Expose some API to the Renderer process ---------
contextBridge.exposeInMainWorld("ipcRenderer", {
  on(...args: Parameters<typeof ipcRenderer.on>) {
    const [channel, listener] = args;
    return ipcRenderer.on(channel, (event, ...args) =>
      listener(event, ...args),
    );
  },
  off(...args: Parameters<typeof ipcRenderer.off>) {
    const [channel, ...omit] = args;
    return ipcRenderer.off(channel, ...omit);
  },
  send(...args: Parameters<typeof ipcRenderer.send>) {
    const [channel, ...omit] = args;
    return ipcRenderer.send(channel, ...omit);
  },
  invoke(...args: Parameters<typeof ipcRenderer.invoke>) {
    const [channel, ...omit] = args;
    return ipcRenderer.invoke(channel, ...omit);
  },

  // You can expose other APTs you need here.
  // ...
} satisfies IpcRendererFacade);

contextBridge.exposeInMainWorld("apm_store", {
  arch: (() => {
    const arch = process.arch;
    if (arch === "x64") {
      return "amd64";
    } else if (arch === "arm64") {
      return "arm64";
    } else {
      return arch;
    }
  })(),
});

contextBridge.exposeInMainWorld("electronUtils", {
  getPathForFile: (file: File): string => webUtils.getPathForFile(file),
});

contextBridge.exposeInMainWorld("windowControls", {
  minimize: () => ipcRenderer.send("window-control-minimize"),
  toggleMaximize: () => ipcRenderer.send("window-control-toggle-maximize"),
  close: () => ipcRenderer.send("window-control-close"),
} satisfies WindowControlBridge);

contextBridge.exposeInMainWorld("updateCenter", {
  open: (storeFilter: StoreFilter = "both"): Promise<UpdateCenterSnapshot> =>
    ipcRenderer.invoke("update-center-open", storeFilter),
  refresh: (storeFilter: StoreFilter = "both"): Promise<UpdateCenterSnapshot> =>
    ipcRenderer.invoke("update-center-refresh", storeFilter),
  ignore: (payload: {
    packageName: string;
    newVersion: string;
  }): Promise<void> => ipcRenderer.invoke("update-center-ignore", payload),
  unignore: (payload: {
    packageName: string;
    newVersion: string;
  }): Promise<void> => ipcRenderer.invoke("update-center-unignore", payload),
  start: (tasks: UpdateCenterStartTask[]): Promise<void> =>
    ipcRenderer.invoke("update-center-start", tasks),
  cancel: (taskKey: string): Promise<void> =>
    ipcRenderer.invoke("update-center-cancel", taskKey),
  getState: (): Promise<UpdateCenterSnapshot> =>
    ipcRenderer.invoke("update-center-get-state"),
  onState: (listener: UpdateCenterStateListener): void => {
    const wrapped = (
      _event: IpcRendererEvent,
      snapshot: UpdateCenterSnapshot,
    ) => {
      listener(snapshot);
    };
    updateCenterStateListeners.set(listener, wrapped);
    ipcRenderer.on("update-center-state", wrapped);
  },
  offState: (listener: UpdateCenterStateListener): void => {
    const wrapped = updateCenterStateListeners.get(listener);
    if (!wrapped) {
      return;
    }

    ipcRenderer.off("update-center-state", wrapped);
    updateCenterStateListeners.delete(listener);
  },
});

// --------- Preload scripts loading ---------
function domReady(
  condition: DocumentReadyState[] = ["complete", "interactive"],
) {
  return new Promise((resolve) => {
    if (condition.includes(document.readyState)) {
      resolve(true);
    } else {
      document.addEventListener("readystatechange", () => {
        if (condition.includes(document.readyState)) {
          resolve(true);
        }
      });
    }
  });
}

const safeDOM = {
  append(parent: HTMLElement, child: HTMLElement) {
    if (!Array.from(parent.children).find((e) => e === child)) {
      return parent.appendChild(child);
    }
  },
  remove(parent: HTMLElement, child: HTMLElement) {
    if (Array.from(parent.children).find((e) => e === child)) {
      return parent.removeChild(child);
    }
  },
};

/**
 * https://tobiasahlin.com/spinkit
 * https://connoratherton.com/loaders
 * https://projects.lukehaas.me/css-loaders
 * https://matejkustec.github.io/SpinThatShit
 */
function useLoading() {
  const className = `loaders-css__square-spin`;
  const styleContent = `
@keyframes square-spin {
  25% { transform: perspective(100px) rotateX(180deg) rotateY(0); }
  50% { transform: perspective(100px) rotateX(180deg) rotateY(180deg); }
  75% { transform: perspective(100px) rotateX(0) rotateY(180deg); }
  100% { transform: perspective(100px) rotateX(0) rotateY(0); }
}
.${className} > div {
  animation-fill-mode: both;
  width: 50px;
  height: 50px;
  background: #fff;
  animation: square-spin 3s 0s cubic-bezier(0.09, 0.57, 0.49, 0.9) infinite;
}
.app-loading-wrap {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #282c34;
  z-index: 9;
}
    `;
  const oStyle = document.createElement("style");
  const oDiv = document.createElement("div");

  oStyle.id = "app-loading-style";
  oStyle.innerHTML = styleContent;
  oDiv.className = "app-loading-wrap";
  oDiv.innerHTML = `<div class="${className}"><div></div></div>`;

  return {
    appendLoading() {
      safeDOM.append(document.head, oStyle);
      safeDOM.append(document.body, oDiv);
    },
    removeLoading() {
      safeDOM.remove(document.head, oStyle);
      safeDOM.remove(document.body, oDiv);
    },
  };
}

// ----------------------------------------------------------------------

const { appendLoading, removeLoading } = useLoading();
domReady().then(appendLoading);

window.onmessage = (ev) => {
  if (ev.data.payload === "removeLoading") removeLoading();
};

setTimeout(removeLoading, 4999);
