import { electronApp, is, optimizer } from "@electron-toolkit/utils";
import Sentry from "@sentry/electron/main";
import { app, BrowserWindow, Menu, nativeImage, shell, Tray } from "electron";
import log from "electron-log/main";
import { autoUpdater } from "electron-updater";
import path, { join } from "path";
import appIcon from "../../resources/diamond.png?asset";
import icon from "../../resources/icon.png?asset";
import {
  default as ErrorTypeCheck,
  default as errorTypeCheck,
} from "../util/errorTypeCheck";
import ipcTypes from "../util/ipcTypes.json";
import ImportJob from "./decoder/decoder";
import LocalServer from "./http-server/http-server";
import { TestQB } from "./quickbooks-desktop/quickbooks-desktop";
import store from "./store/store";
import { GetAllEnvFiles } from "./watcher/watcher";

Sentry.init({
  dsn: "https://ba41d22656999a8c1fd63bcb7df98650@o492140.ingest.us.sentry.io/4509074139447296",
});

log.initialize();
const isMac = process.platform === "darwin";
let isAppQuitting = false; //Needed on Mac as an override to allow us to fully quit the app.
// Initialize the server
const localServer = new LocalServer();
const gotTheLock = app.requestSingleInstanceLock();

function createWindow(): void {
  // Create the browser window.
  const { width, height, x, y } = store.get("app.windowBounds") as {
    width: number;
    height: number;
    x: number | undefined;
    y: number | undefined;
  };

  const mainWindow = new BrowserWindow({
    width,
    height,
    x,
    y,
    show: false,
    minWidth: 600,
    minHeight: 400,
    //autoHideMenuBar: true,
    ...(process.platform === "linux" ? { icon } : {}),
    title: "Shop Partner",
    webPreferences: {
      preload: join(__dirname, "../preload/index.js"),
      sandbox: false,
      devTools: true,
    },
  });

  const template: Electron.MenuItemConstructorOptions[] = [
    // { role: 'appMenu' }
    ...(isMac
      ? [
          {
            label: app.name,
            submenu: [
              { role: "about" },
              { type: "separator" },
              { role: "services" },
              { type: "separator" },
              { role: "hide" },
              { role: "hideOthers" },
              { role: "unhide" },
              { type: "separator" },
              { role: "quit" },
            ],
          },
        ]
      : []),
    // { role: 'fileMenu' }
    {
      label: "File",
      submenu: [isMac ? { role: "close" } : { role: "quit" }],
    },
    // { role: 'editMenu' }
    {
      label: "Edit",
      submenu: [
        { role: "undo" },
        { role: "redo" },
        { type: "separator" },
        { role: "cut" },
        { role: "copy" },
        { role: "paste" },
        ...(isMac
          ? [
              { role: "pasteAndMatchStyle" },
              { role: "delete" },
              { role: "selectAll" },
              { type: "separator" },
              {
                label: "Speech",
                submenu: [{ role: "startSpeaking" }, { role: "stopSpeaking" }],
              },
            ]
          : [{ role: "delete" }, { type: "separator" }, { role: "selectAll" }]),
      ],
    },
    // { role: 'viewMenu' }
    {
      label: "View",
      submenu: [
        { role: "reload" },
        { role: "forceReload" },
        { role: "toggleDevTools" },
        { type: "separator" },
        { role: "resetZoom" },
        { role: "zoomIn" },
        { role: "zoomOut" },
        { type: "separator" },
        { role: "togglefullscreen" },
      ],
    },
    {
      label: "Application",
      submenu: [
        {
          label: "Open on Startup",
          checked: store.get("app.openOnStartup") as boolean,
          type: "checkbox",
          click: (): void => {
            const currentSetting = store.get("app.openOnStartup") as boolean;
            store.set("app.openOnStartup", !currentSetting);
            log.info("Open on startup set to", !currentSetting);
            app.setLoginItemSettings({
              enabled: true, //This is a windows only command. Updates the task manager and registry.
              openAtLogin: !currentSetting,
            });
          },
        },
      ],
    },
    // { role: 'windowMenu' }
    {
      label: "Window",
      submenu: [
        { role: "minimize" },
        { role: "zoom" },
        ...(isMac
          ? [
              { type: "separator" },
              { role: "front" },
              { type: "separator" },
              { role: "window" },
            ]
          : [{ role: "close" }]),
      ],
    },

    {
      label: "Development",
      submenu: [
        {
          label: "Check for updates",
          click: (): void => {
            autoUpdater.checkForUpdates();
          },
        },
        {
          label: "Open Log Folder",
          click: (): void => {
            /* action for item 1 */
            shell.openPath(log.transports.file.getFile().path);
          },
        },
        {
          label: "Clear Log",
          click: (): void => {
            log.transports.file.getFile().clear();
          },
        },
        {
          label: "Open Config",
          click: (): void => {
            shell.openPath(path.dirname(store.path));
          },
        },
        {
          label: "Log the Store",
          click: (): void => {
            log.debug("Store Contents" + JSON.stringify(store.store, null, 4));
          },
        },
        {
          type: "separator",
        },
        {
          label: "Temp Test Action",
          click: (): void => {
            TestQB();
          },
        },
        {
          label: "Decode Hardcoded Estimate",
          click: (): void => {
            ImportJob(`C:\\EMS\\CCC\\9ee762f4.ENV`);
          },
        },
        {
          label: "Add All Estimes in watched directories",
          click: (): void => {
            GetAllEnvFiles().forEach((file) => ImportJob(file));
          },
        },
      ],
    },
  ];

  const menu: Electron.Menu = Menu.buildFromTemplate(template);
  Menu.setApplicationMenu(menu);

  // Store window properties for later
  const storeWindowState = (): void => {
    const [width, height] = mainWindow.getSize();
    const [x, y] = mainWindow.getPosition();
    store.set("app.windowBounds", { width, height, x, y });
  };
  mainWindow.on("resized", storeWindowState);
  mainWindow.on("maximize", storeWindowState);
  mainWindow.on("unmaximize", storeWindowState);
  mainWindow.on("moved", storeWindowState);

  mainWindow.on("ready-to-show", () => {
    mainWindow.show();
    //Start the HTTP server.
    // Start the local HTTP server
    try {
      localServer.start();
    } catch (error) {
      log.error("Failed to start HTTP server:", errorTypeCheck(error));
    }
  });

  mainWindow.on("close", (event: Electron.Event) => {
    if (!isAppQuitting) {
      event.preventDefault(); // Prevent the window from closing
      mainWindow.hide();
    }
  });

  mainWindow.webContents.setWindowOpenHandler((details) => {
    shell.openExternal(details.url);
    return { action: "deny" };
  });

  // HMR for renderer base on electron-vite cli.
  // Load the remote URL for development or the local html file for production.
  if (is.dev && process.env["ELECTRON_RENDERER_URL"]) {
    mainWindow.loadURL(process.env["ELECTRON_RENDERER_URL"]);
  } else {
    mainWindow.loadFile(join(__dirname, "../renderer/index.html"));
  }
  if (import.meta.env.DEV) {
    mainWindow.webContents.openDevTools();
  }
}

if (!gotTheLock) {
  app.quit(); // Quit the app if another instance is already running
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.whenReady().then(async () => {
  // Set app user model id for windows
  electronApp.setAppUserModelId("com.electron");

  // Default open or close DevTools by F12 in development
  // and ignore CommandOrControl + R in production.
  // see https://github.com/alex8088/electron-toolkit/tree/master/packages/utils
  app.on("browser-window-created", (_, window) => {
    optimizer.watchWindowShortcuts(window);
  });

  // Add this event handler for second instance
  app.on("second-instance", () => {
    // Someone tried to run a second instance, we should focus our window
    openMainWindow();
  });

  //Dynamically load ipcMain handlers once ready.
  try {
    // Replace 'path/to/your/file' with the actual path to your file
    const module = await import("./ipc/ipcMainConfig");

    // You can now use anything exported from the module
    // For example:
    // module.someFunction()
    log.debug("Successfully loaded ipcMainConfig", module);
  } catch (error) {
    log.error("Failed to load ipcMainconfig", {
      ...ErrorTypeCheck(error),
    });
  }

  //Create Tray
  const trayicon = nativeImage.createFromPath(appIcon);
  const tray = new Tray(trayicon.resize({ width: 16 }));
  const contextMenu = Menu.buildFromTemplate([
    {
      label: "Show App",
      click: (): void => {
        openMainWindow();
      },
    },
    {
      label: "Quit",
      click: (): void => {
        app.quit(); // actually quit the app.
      },
    },
  ]);

  tray.setContextMenu(contextMenu);

  //Check for app updates.
  autoUpdater.logger = log;
  // if (import.meta.env.DEV) {
  //   // Useful for some dev/debugging tasks, but download can
  //   // not be validated becuase dev app is not signed
  //   autoUpdater.updateConfigPath = path.join(
  //     __dirname,
  //     "../../dev-app-update.yml",
  //   );
  //   autoUpdater.forceDevUpdateConfig = true;
  //   autoUpdater.autoDownload = false;
  // }
  autoUpdater.on("checking-for-update", () => {
    log.info("Checking for update...");
    const mainWindow = BrowserWindow.getAllWindows()[0];
    mainWindow?.webContents.send(ipcTypes.toRenderer.updates.checking);
  });
  autoUpdater.on("update-available", (info) => {
    log.info("Update available.", info);
    const mainWindow = BrowserWindow.getAllWindows()[0];
    mainWindow?.webContents.send(ipcTypes.toRenderer.updates.available, info);
  });
  autoUpdater.on("download-progress", (progress) => {
    log.info(`Download speed: ${progress.bytesPerSecond}`);
    log.info(`Downloaded ${progress.percent}%`);
    log.info(`Total downloaded ${progress.transferred}/${progress.total}`);
    const mainWindow = BrowserWindow.getAllWindows()[0];
    mainWindow?.webContents.send(
      ipcTypes.toRenderer.updates.downloading,
      progress,
    );
  });
  autoUpdater.on("update-downloaded", (info) => {
    log.info("Update downloaded", info);
    const mainWindow = BrowserWindow.getAllWindows()[0];
    mainWindow?.webContents.send(ipcTypes.toRenderer.updates.downloaded, info);
  });
  //The update itself will run when the bodyshop record is queried to know what release channel to use.
  createWindow();

  app.on("activate", function () {
    openMainWindow();
  });
});

// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on("window-all-closed", () => {
  if (process.platform !== "darwin") {
    app.quit(); //Disable the quit.
  }
});

app.on("before-quit", () => {
  localServer.stop();
  const currentSetting = store.get("app.openOnStartup") as boolean;
  store.set("app.openOnStartup", !currentSetting);
  log.info("Open on startup set to", !currentSetting);
  app.setLoginItemSettings({
    enabled: true, //This is a windows only command. Updates the task manager and registry.
    openAtLogin: !currentSetting,
  });

  isAppQuitting = true;
});

function openMainWindow(): void {
  const mainWindow = BrowserWindow.getAllWindows()[0];
  if (mainWindow) {
    mainWindow.show();
  } else {
    createWindow();
  }
}
