// printer-manager.js - Manages multiple printer connections through MongoDB
import { PrinterClient } from "./printerclient.js";
import { loadConfig } from "../config.js";
import log4js from "log4js";
import { sendIPC } from "../electron/ipc.js";
import { startWaiting, stopWaiting } from "../spinner.js";
// Load configuration
const config = loadConfig();

const logger = log4js.getLogger("Printer Manager");
logger.level = config.logLevel;

export class PrinterManager {
  constructor(socketClient) {
    this.socketClient = socketClient;
    this.printerClients = new Map();
    this.printers = [];
  }

  async reloadPrinters() {
    startWaiting("Reloading printers...", logger);
    try {
      this.printers = await this.socketClient.listObjects({
        objectType: "printer",
        filter: { host: this.socketClient.id },
        populate: [
          "currentFilamentStock",
          "currentJob",
          "currentSubJob",
          "queue",
        ],
      });

      sendIPC("setPrinters", this.printers);

      var removedPrintersCount = 0;

      // Remove printer clients that are no longer in the printers list
      const printerIds = this.printers.map((printer) => printer._id);
      for (const [printerId, printerClient] of this.printerClients.entries()) {
        if (!printerIds.includes(printerId)) {
          // Close the connection before removing
          if (printerClient.socket) {
            printerClient.shouldReconnect = false;
            printerClient.socket.close();
          }
          this.printerClients.delete(printerId);
          logger.info(`Removed printer client for printer ID: ${printerId}`);
          removedPrintersCount++;
        }
      }

      var addedPrintersCount = 0;
      // Add new printer clients for printers not in the printerClients map
      for (const printer of this.printers) {
        const printerId = printer._id;
        if (!this.printerClients.has(printerId)) {
          const printerClient = new PrinterClient(printer, this);
          await printerClient.connect();
          this.printerClients.set(printerId, printerClient);
          logger.info(`Added printer client for printer ID: ${printerId}`);
          addedPrintersCount++;
        }
      }

      await stopWaiting();

      logger.debug("Printers added:", addedPrintersCount);
      logger.debug("Printers removed:", removedPrintersCount);
    } catch (error) {
      await stopWaiting();
      logger.error("Failed to update printers:", error);
      this.printers = [];
    }
  }

  async handlePrinterAction(id, action, callback) {
    logger.debug("Running printer action...", action);
    const printer = this.getPrinterClient(id);
    switch (action.type) {
      case "setTemperature":
        const setTempResult = await printer.setTemperature(action.data);
        callback(setTempResult);
        return;

      case "homeAxis":
        const homeAxisResult = await printer.homeAxis(action.data);
        callback(homeAxisResult);
        return;

      case "moveAxis":
        const moveAxisResult = await printer.moveAxis(action.data);
        callback(moveAxisResult);
        return;

      case "restartPrinterFirmware":
        const restartPrinterFirmwareResult =
          await printer.restartPrinterFirmware();
        callback(restartPrinterFirmwareResult);
        return;

      case "restartPrinter":
        const restartPrinterResult = await printer.restartPrinter();
        callback(restartPrinterResult);
        return;
      case "restartMoonraker":
        const restartMoonrakerResult = await printer.restartMoonraker();
        callback(restartMoonrakerResult);
        return;
      case "deploy":
        const deployResult = await printer.deploySubJob(action.data);
        callback(deployResult);
        return;
      case "startQueue":
        const startQueueResult = await printer.startQueue();
        callback(startQueueResult);
        return;
      case "pauseJob":
        const pauseJobResult = await printer.pauseJob();
        callback(pauseJobResult);
        return;
      case "resumeJob":
        const resumeJobResult = await printer.resumeJob();
        callback(resumeJobResult);
        return;
      case "cancelJob":
        const cancelJobResult = await printer.cancelJob();
        callback(cancelJobResult);
        return;
      case "unloadFilamentStock":
        const unloadFilamentStockResult = await printer.unloadFilamentStock();
        callback(unloadFilamentStockResult);
        return;
      case "loadFilamentStock":
        const loadFilamentStockResult = await printer.loadFilamentStock(
          action.data.filamentStock,
        );
        callback(loadFilamentStockResult);
        return;
    }
    callback({ error: "Unknown command." });
  }

  async handlePrinterUpdate(id, data) {
    logger.debug("Handling printer update for id:", id);
    const printer = this.getPrinterClient(id);
    if (printer) {
      await printer.updatePrinter(data.object);
    }
  }

  getPrinterClient(printerId) {
    return this.printerClients.get(printerId);
  }

  getAllPrinterClients() {
    return this.printerClients.values();
  }

  // Close all printer connections
  async closeAllConnections() {
    startWaiting(
      `Closing all printer connections... current count: ${this.printerClients.size}`,
      logger,
    );
    try {
      const clients = Array.from(this.printerClients.values());

      for (const printerClient of clients) {
        try {
          // Ensure we never auto-reconnect after a manual close-all
          printerClient.shouldReconnect = false;
          await printerClient.disconnect();
          logger.info(
            `Disconnected printer client ${printerClient?.id || "unknown"}`,
          );
        } catch (error) {
          logger.error(
            `Failed to disconnect printer client ${
              printerClient?.id || "unknown"
            }:`,
            error,
          );
        }
      }

      console.log("Printer clients:", this.printerClients);

      // Clear local references so no stale clients remain
      this.printerClients.clear();
      this.printers = [];

      logger.info(
        `All printer connections closed. Remaining clients: ${this.printerClients.size}`,
      );
      await stopWaiting();
    } catch (error) {
      await stopWaiting();
      throw error;
    }
  }
}
