import { z } from "zod";
import { apptileContextZodSchema } from "../../../utils";
import { defineToolSpec, logger } from "./utils";
import { applyPatch, applyPatches } from "diff";
import { readFile, writeFile } from "fs/promises";
import path from "path";
import chalk from "chalk";

// export const applyPatchesToFile = defineToolSpec({
//   name: "apply_multiple_patches_to_file",
//   description: `Apply multiple patches on a single file in a plugin's directory and get the result of applying the patch. The file paths 
//   should be provided relative to the plugin's directory. The file reading tools append line numbers in the format <line_number>:<space>. 
//   This prefix is not actually a part of the file and you should generate the patch as if the line number and the trailing 
//   colon and space after it were not there.`,
//   inputSchema: {
//     plugin_name: z.string().describe("Name of the plugin inside whose directory the target file will be looked for."),
//     file_path: z.string().describe("The relative path of the file within the plugin's directory to which the patch should be applied."),
//     apptile_context: apptileContextZodSchema,
//     patch: z.string().describe(`The patch to be applied. This should be a 'unified diff' in the format:
// @@ -line,count +line,count @@
// - old line
// + new line

// @@ -line,count +line,count @@
// - old line
// + new line
// `)
//   },
//   async cb(inputVars) {
//     logger.info("Running tool " + chalk.green("apply_multiple_patches_to_file"));
//     const result = await new Promise<string>(async (resolve) => {
//       try {
//         if (!inputVars.apptile_context?.plugins_dir) {
//           resolve("Error: apptile_context was not resolved in the system. Something went wrong!");
//         } else {
//           const fullFilePath = path.resolve(
//             inputVars.apptile_context.plugins_dir, 
//             inputVars.plugin_name, 
//             inputVars.file_path
//           );
//           let hasError = false;
//           let fileContents = await readFile(fullFilePath, "utf8");
//           applyPatches(inputVars.patch, {
//             loadFile: (patch, callback) => {
//                 callback(undefined, fileContents);
//             },
//             patched: (patch, patchedContent, callback) => {
//               if (patchedContent !== false) {
//                 fileContents = patchedContent;
//                 callback(null);
//               } else {
//                 hasError = true;
//                 callback("Error: `diff` returned false when calling applyPatch")
//               }
//             },
//             complete: async (err) => {
//               if (err) {
//                 resolve("Error: Failed to apply patch " + err);
//               } else {
//                 await writeFile(fullFilePath, fileContents);
//                 resolve("Patch applied successfully! ")
//               }
//             }
//           });
//         }
//       } catch (err) {
//         resolve("Error: Could not apply the patch: " + err);
//       }
//     });

//     return {
//       content: [{
//         type: "text",
//         text: result
//       }]
//     };
//   }
// })

export const applyPatchToFile = defineToolSpec({
  name: "apply_patch_to_file",
  description: `Apply patch on a file in a plugin's directory and get the result of applying the patch. The file paths 
  should be provided relative to the plugin's directory. The file reading tools append line numbers in the format <line_number>:<space>. 
  This prefix is not actually a part of the file and you should generate the patch as if the line number and the trailing 
  colon and space after it were not there.`,
  inputSchema: {
    plugin_name: z.string().describe("Name of the plugin inside whose directory the target file will be looked for."),
    file_path: z.string().describe("The relative path of the file within the plugin's directory to which the patch should be applied."),
    apptile_context: apptileContextZodSchema,
    patch: z.string().describe(`The patch to be applied. This should be a 'unified diff' in the format:
@@ -line,count +line,count @@
- old line
+ new line`)
  },
  async cb(inputVars) {
    logger.info("Running tool: " + chalk.green("apply_patch_to_file"));
    const result = await new Promise<string>(async (resolve) => {
      try {
        if (!inputVars.apptile_context?.plugins_dir) {
          resolve("Error: apptile_context was not resolved in the system. Something went wrong!");
        } else {
          const fullFilePath = path.resolve(
            inputVars.apptile_context.plugins_dir, 
            inputVars.plugin_name, 
            inputVars.file_path
          );
          const fileContents = await readFile(fullFilePath, "utf8");
          const patchedContents = applyPatch(fileContents, inputVars.patch, {
            fuzzFactor: 3,
            autoConvertLineEndings: true
          });
          if (patchedContents === false) {
            resolve("Error: `diff` returned false when calling applyPatch.");
          } else {
            await writeFile(fullFilePath, patchedContents);
            resolve("Patch applied successfully!");
          }
        }
      } catch (err) {
        resolve("Error: Could not apply the patch: " + err);
      }
    });

    return {
      content: [{
        type: "text",
        text: result
      }]
    }
  }
});