const { app, BrowserWindow, ipcMain, dialog } = require("electron");
const pdfParse = require("pdf-parse");
const mammoth = require("mammoth");
const fs = require("fs");
const path = require("path");
const os = require("os");
const { queryOpenAI } = require("./chatgpt.js");
const axios = require("axios");
const fsExtra = require("fs-extra");

let fetch;
import("node-fetch").then((module) => {
  fetch = module.default;
});
const unzipper = require("unzipper");

let win;

function promptUserForApiKey() {
  // Create a new window to prompt the user for the API key
  const promptWindow = new BrowserWindow({
    // Window configuration for the prompt
    width: 500,
    height: 200,
    webPreferences: {
      nodeIntegration: true,
      contextIsolation: false, // Consider security implications
    },
  });

  // Handle the API key submission from the prompt window
  ipcMain.on("submit-api-key", (event, apiKey) => {
    if (apiKey) {
      saveApiKey(apiKey);
      promptWindow.close();
      createWindow(); // Proceed to create the main window
    } else {
      // Handle invalid input or user cancellation
      promptWindow.close();
    }
  });
}

function loadApiKey() {
  const configPath = path.join(os.homedir(), ".config", "fabric", ".env");
  if (fs.existsSync(configPath)) {
    const envContents = fs.readFileSync(configPath, { encoding: "utf8" });
    const matches = envContents.match(/^OPENAI_API_KEY=(.*)$/m);
    if (matches && matches[1]) {
      return matches[1];
    }
  }
  return null;
}

function saveApiKey(apiKey) {
  const configPath = path.join(os.homedir(), ".config", "fabric");
  const envFilePath = path.join(configPath, ".env");

  if (!fs.existsSync(configPath)) {
    fs.mkdirSync(configPath, { recursive: true });
  }

  fs.writeFileSync(envFilePath, `OPENAI_API_KEY=${apiKey}`);
  process.env.OPENAI_API_KEY = apiKey; // Set for current session
}

function ensureFabricFoldersExist() {
  return new Promise(async (resolve, reject) => {
    const fabricPath = path.join(os.homedir(), ".config", "fabric");
    const patternsPath = path.join(fabricPath, "patterns");

    try {
      if (!fs.existsSync(fabricPath)) {
        fs.mkdirSync(fabricPath, { recursive: true });
      }

      if (!fs.existsSync(patternsPath)) {
        fs.mkdirSync(patternsPath, { recursive: true });
        await downloadAndUpdatePatterns(patternsPath);
      }
      resolve(); // Resolve the promise once everything is set up
    } catch (error) {
      console.error("Error ensuring fabric folders exist:", error);
      reject(error); // Reject the promise if an error occurs
    }
  });
}

async function downloadAndUpdatePatterns(patternsPath) {
  try {
    const response = await axios({
      method: "get",
      url: "https://github.com/danielmiessler/fabric/archive/refs/heads/main.zip",
      responseType: "arraybuffer",
    });

    const zipPath = path.join(os.tmpdir(), "fabric.zip");
    fs.writeFileSync(zipPath, response.data);
    console.log("Zip file written to:", zipPath);

    const tempExtractPath = path.join(os.tmpdir(), "fabric_extracted");
    fsExtra.emptyDirSync(tempExtractPath);

    await fsExtra.remove(patternsPath); // Delete the existing patterns directory

    await fs
      .createReadStream(zipPath)
      .pipe(unzipper.Extract({ path: tempExtractPath }))
      .promise();

    console.log("Extraction complete");

    const extractedPatternsPath = path.join(
      tempExtractPath,
      "fabric-main",
      "patterns"
    );

    await fsExtra.copy(extractedPatternsPath, patternsPath);
    console.log("Patterns successfully updated");

    // Inform the renderer process that the patterns have been updated
    win.webContents.send("patterns-updated");
  } catch (error) {
    console.error("Error downloading or updating patterns:", error);
  }
}

function checkApiKeyExists() {
  const configPath = path.join(os.homedir(), ".config", "fabric", ".env");
  return fs.existsSync(configPath);
}

function getPatternFolders() {
  const patternsPath = path.join(os.homedir(), ".config", "fabric", "patterns");
  return fs
    .readdirSync(patternsPath, { withFileTypes: true })
    .filter((dirent) => dirent.isDirectory())
    .map((dirent) => dirent.name);
}

function getPatternContent(patternName) {
  const patternPath = path.join(
    os.homedir(),
    ".config",
    "fabric",
    "patterns",
    patternName,
    "system.md"
  );
  try {
    return fs.readFileSync(patternPath, "utf8");
  } catch (error) {
    console.error("Error reading pattern file:", error);
    return "";
  }
}

function createWindow() {
  win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      contextIsolation: true,
      nodeIntegration: false,
      preload: path.join(__dirname, "preload.js"),
    },
  });

  win.loadFile("index.html");

  win.on("closed", () => {
    win = null;
  });
}
ipcMain.on("process-complex-file", (event, filePath) => {
  const extension = path.extname(filePath).toLowerCase();
  let fileProcessPromise;

  if (extension === ".pdf") {
    const dataBuffer = fs.readFileSync(filePath);
    fileProcessPromise = pdfParse(dataBuffer).then((data) => data.text);
  } else if (extension === ".docx") {
    fileProcessPromise = mammoth
      .extractRawText({ path: filePath })
      .then((result) => result.value)
      .catch((err) => {
        console.error("Error processing DOCX file:", err);
        throw new Error("Error processing DOCX file.");
      });
  } else {
    event.reply("file-response", "Error: Unsupported file type");
    return;
  }

  fileProcessPromise
    .then((extractedText) => {
      // Sending the extracted text back to the frontend.
      event.reply("file-response", extractedText);
    })
    .catch((error) => {
      // Handling any errors during file processing and sending them back to the frontend.
      event.reply("file-response", `Error processing file: ${error.message}`);
    });
});

ipcMain.on("start-query-openai", async (event, system, user) => {
  if (system == null || user == null) {
    console.error("Received null for system or user message");
    event.reply("openai-response", "Error: System or user message is null.");
    return;
  }
  try {
    await queryOpenAI(system, user, (message) => {
      event.reply("openai-response", message);
    });
  } catch (error) {
    console.error("Error querying OpenAI:", error);
    event.reply("no-api-key", "Error querying OpenAI.");
  }
});

// Example of using ipcMain.handle for asynchronous operations
ipcMain.handle("get-patterns", async (event) => {
  try {
    return getPatternFolders();
  } catch (error) {
    console.error("Failed to get patterns:", error);
    return [];
  }
});

ipcMain.on("update-patterns", () => {
  const patternsPath = path.join(os.homedir(), ".config", "fabric", "patterns");
  downloadAndUpdatePatterns(patternsPath);
});

ipcMain.handle("get-pattern-content", async (event, patternName) => {
  try {
    return getPatternContent(patternName);
  } catch (error) {
    console.error("Failed to get pattern content:", error);
    return "";
  }
});

ipcMain.handle("save-api-key", async (event, apiKey) => {
  try {
    const configPath = path.join(os.homedir(), ".config", "fabric");
    if (!fs.existsSync(configPath)) {
      fs.mkdirSync(configPath, { recursive: true });
    }

    const envFilePath = path.join(configPath, ".env");
    fs.writeFileSync(envFilePath, `OPENAI_API_KEY=${apiKey}`);
    process.env.OPENAI_API_KEY = apiKey;

    return "API Key saved successfully.";
  } catch (error) {
    console.error("Error saving API key:", error);
    throw new Error("Failed to save API Key.");
  }
});

app.whenReady().then(async () => {
  try {
    const apiKey = loadApiKey();
    if (!apiKey) {
      promptUserForApiKey();
    } else {
      process.env.OPENAI_API_KEY = apiKey;
      createWindow();
    }
    await ensureFabricFoldersExist(); // Ensure fabric folders exist
    createWindow(); // Create the application window

    // After window creation, check if the API key exists
    if (!checkApiKeyExists()) {
      console.log("API key is missing. Prompting user to input API key.");
      // Optionally, directly invoke a function here to show a prompt in the renderer process
      win.webContents.send("request-api-key");
    }
  } catch (error) {
    console.error("Failed to initialize fabric folders:", error);
    // Handle initialization failure (e.g., close the app or show an error message)
  }
});

app.on("window-all-closed", () => {
  if (process.platform !== "darwin") {
    app.quit();
  }
});

app.on("activate", () => {
  if (win === null) {
    createWindow();
  }
});