import express from "express";
import {
  listWorkspaces,
  openWithFinder,
  openWithCode,
  cloneApp,
  generateApptileConfig,
  getAppLocation,
  getLocalBundles,
  getAppConfigsFromFS,
  cloneAppEc2,
  createAppFolder,
  getFileTree,
  manageSourceFileOrFolder,
} from "../controllers/workspace";
import { getRemoteCode } from "../controllers/projectSetup";
import bodyParser from "body-parser";
import {
  AgentProgramType,
  closeAppByLocation,
  getOpenApp,
  insertOrUpdateOpenapp,
} from "../database/cliconfig";
import { npmInstall, podInstall } from "../controllers/projectSetup";
import { sendLog } from "../websocket";
import axios from "axios";
import chalk from "chalk";
import { makeHeadersWithCookie, getMidUrlIntegrationsValue } from "../utils";
import path from "path";
import fs, { constants } from "fs";
import fsPromises from "fs/promises";
// import { getMidUrlIntegrationsValue } from '../controllers/claudeAgent';
export const jsonParser = bodyParser.json();
const router = express.Router();
// import workspaceMiddleware from '../middlewares/workspace';

router.delete(
  "/workspaces/:workspaceId/closeApp",
  jsonParser,
  async (req, res, next) => {
    const fullPath = req.body.fullPath;
    closeAppByLocation(fullPath);
    res.status(200);
    res.end();
  }
);

router.post("/operation/opencode/:location", async (req, res, next) => {
  try {
    const location = decodeURIComponent(req.params.location);
    await openWithCode(location);
    res.status(200);
    res.end();
  } catch (err) {
    console.error("Failed when trying to open location with finder");
    next(err);
  }
});

router.post("/operation/openfinder/:location", async (req, res, next) => {
  try {
    const location = decodeURIComponent(req.params.location);
    await openWithFinder(location);
    res.status(200);
    res.end();
  } catch (err) {
    console.error("Failed when trying to open location with finder");
    next(err);
  }
});

router.get("/workspaces/list", async (req, res, next) => {
  try {
    const response = await listWorkspaces();
    res.json(response);
  } catch (err: any) {
    console.error("Failure in GET /workspace");
    next(err);
  }
});

router.post("/workspaces", async (req, res) => {
  // add entry in workspace table to link a filesystem path and name
  // link workspace to apptile-server url
  // clone sdk repository and checkout specified branch
});

router.post(
  "/workspaces/:workspaceId/cloneApp",
  jsonParser,
  async (req, res, next) => {
    try {
      const workspace = parseInt(req.params.workspaceId);
      // TODO(add types to manifest and appIntegrations)
      const manifest = req.body.manifest;
      const appIntegrations = req.body.appIntegrations;
      const apptileConfig = req.body.apptileConfig;

      if (process.env.NODE_ENV === "development") {
        cloneApp(workspace, manifest, appIntegrations, apptileConfig);
      } else {
        cloneAppEc2(workspace, manifest, appIntegrations, apptileConfig);
      }

      res.status(200);
      res.end();
    } catch (err) {
      console.error("Failed to clone app");
      next(err);
    }
  }
);

router.get("/workspaces/:workspaceId/app/:appId", async (req, res, next) => {
  const workspaceId = req.params.workspaceId;
  const appId = req.params.appId;
  try {
    const appLocation = await getAppLocation(parseInt(workspaceId), appId);
    const localBundles = await getLocalBundles(appLocation);

    res.json({
      appId,
      appRoot: appLocation,
      codePushBundles: localBundles,
    });
  } catch (err) {
    next(err);
  }
});

router.post(
  "/workspaces/:workspaceId/openApp/:appId",
  async (req, res, next) => {
    // find the folder for appid in the workspace
    const appId = req.params.appId;
    const workspaceId = req.params.workspaceId;

    try {
      const appRoot = await getAppLocation(parseInt(workspaceId), appId);
      insertOrUpdateOpenapp(workspaceId, appId, appRoot);
      res.status(201);
      res.end();
    } catch (err) {
      console.error("Failed to open app");
      next(err);
    }
  }
);

router.post("/app/:appId/regenerateAppConfig", async (req, res, next) => {
  try {
    const appId = req.params.appId;
    const existingConfig = await getAppConfigsFromFS(appId);
    await generateApptileConfig(existingConfig);
    res.status(200);
    res.end();
  } catch (error: any) {
    console.error("Failed to regenerate app config", error);
    res.status(400).json({ error: error.message });
  }
});

router.post("/app/:appId/npmInstall", async (req, res, next) => {
  sendLog("Starting npm install");
  const appId = req.params.appId;
  try {
    const app = await getOpenApp(appId);
    if (app) {
      await npmInstall(app.repoPath);
    } else {
      throw new Error(
        "Unable to run npm install for an app that is not open " + appId
      );
    }
    res.status(201);
    res.end();
  } catch (err) {
    console.error("failed to do npm install", err);
    next(err);
  }
});

router.post("/app/:appId/podInstall", async (req, res, next) => {
  sendLog("Starting pod install");
  const appId = req.params.appId;
  try {
    const app = await getOpenApp(appId);
    if (app) {
      await podInstall(app.repoPath);
    } else {
      throw new Error(
        "Unable to run npm install for an app that is not open " + appId
      );
    }
    res.status(201);
    res.end();
  } catch (err) {
    console.error("failed to do npm install", err);
    next(err);
  }
});

router.get("/shoplazza-temp-proxy/*", async (req, res, next) => {
  // const query = req.query;
  let config = {
    method: "get",
    maxBodyLength: Infinity,
    url: "https://shoplazza-test-store1.myshoplaza.com/api/collections/9f36baea-e6b3-4f1e-8bb7-18c0b44a3e2a/cps?page=0&limit=8",
    headers: {},
  };
  try {
    const { data } = await axios.request(config);
    res.json(data);
  } catch (err) {
    next(err);
  }
});

router.get("/app/:appId/remoteCode", (req, res, next) => {
  const appId = req.params.appId;

  getRemoteCode(appId)
    .then((result) => {
      if (result.err) {
        // Set content type to prevent curl from saving error as zip file
        res.setHeader("Content-Type", "application/json");
        return res.status(404).json({ error: result.err });
      }

      if (result.zipPath) {
        const filename = path.basename(result.zipPath);
        res.download(result.zipPath, filename, (err) => {
          // Delete the zip file after it has been sent (whether successful or not)
          try {
            // Make sure zipPath exists and is not undefined
            if (result.zipPath && fs.existsSync(result.zipPath)) {
              fs.unlinkSync(result.zipPath);
            }
          } catch (e) {
            console.error(
              `Failed to delete temporary zip file: ${
                result.zipPath || "unknown"
              }`
            );
          }

          if (err) {
            // Just continue - don't try to send another response
            // as headers have already been sent
          }
        });
      } else {
        return res.status(500).json({ error: "Failed to create zip file" });
      }
    })
    .catch((err) => {
      console.error("Error in remoteCode download endpoint:", err);
      next(err);
    });
});

router.get("/createLog", async (req, res, next) => {
  console.log(chalk.yellow("LOG: " + req.headers["x-telemetry-log"]));
  res.status(200);
  res.end();
});

// Proxy routes for Supabase API to handle CORS issues
// OAuth token endpoint
router.post(
  "/supabase-proxy/oauth/token",
  bodyParser.urlencoded({ extended: true }),
  async (req, res, next) => {
    console.log("Request body: ", req.body);
    try {
      const response = await axios.post(
        "https://api.supabase.com/v1/oauth/token",
        req.body,
        {
          headers: {
            "Content-Type": "application/x-www-form-urlencoded",
          },
        }
      );

      res.status(response.status).json(response.data);
    } catch (error) {
      console.error("Error proxying request to Supabase:", error);
      if (axios.isAxiosError(error) && error.response) {
        res.status(error.response.status).json(error.response.data);
      } else {
        res.status(500).json({ error: "Failed to proxy request to Supabase" });
      }
    }
  }
);

// Projects list endpoint
router.get("/supabase-proxy/projects", async (req, res, next) => {
  try {
    const response = await axios.get("https://api.supabase.com/v1/projects", {
      headers: {
        Authorization: req.headers.authorization,
        "Content-Type": "application/json",
      },
    });

    res.status(response.status).json(response.data);
  } catch (error) {
    console.error("Error proxying request to Supabase:", error);
    if (axios.isAxiosError(error) && error.response) {
      res.status(error.response.status).json(error.response.data);
    } else {
      res.status(500).json({ error: "Failed to proxy request to Supabase" });
    }
  }
});

// Project API keys endpoint
router.get(
  "/supabase-proxy/projects/:projectId/api-keys",
  async (req, res, next) => {
    try {
      const projectId = req.params.projectId;
      const response = await axios.get(
        `https://api.supabase.com/v1/projects/${projectId}/api-keys`,
        {
          headers: {
            Authorization: req.headers.authorization,
            "Content-Type": "application/json",
          },
        }
      );

      res.status(response.status).json(response.data);
    } catch (error) {
      console.error("Error proxying request to Supabase:", error);
      if (axios.isAxiosError(error) && error.response) {
        res.status(error.response.status).json(error.response.data);
      } else {
        res.status(500).json({ error: "Failed to proxy request to Supabase" });
      }
    }
  }
);

// Supabase token refresh endpoint
router.post("/supabase-proxy/refresh-token", jsonParser, async (req, res) => {
  try {
    const appId = req.body.appId;
    if (!appId) {
      res.status(400).json({ error: "App ID is required" });
      return;
    }

    const app = await getOpenApp(appId);
    if (!app) {
      res.status(404).json({ error: "App not found or not open" });
      return;
    }

    // Get app configuration
    const apptileConfig = await getAppConfigsFromFS(appId);
    const header = makeHeadersWithCookie(app.apptilecookie, {});

    // First, get the current credentials
    process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
    const credsResponse = await fetch(
      `${
        apptileConfig.APPTILE_BACKEND_URL
      }/${getMidUrlIntegrationsValue()}/${appId}/appIntegrations/supabase/credentials`,
      {
        method: "GET",
        headers: { ...header.headers },
      }
    );

    if (!credsResponse.ok) {
      throw new Error(
        `Failed to fetch credentials: ${credsResponse.statusText}`
      );
    }

    const credsData = await credsResponse.json();

    if (
      !credsData.SUPABASE_REFRESH_TOKEN ||
      !credsData.APPTILE_SUPABASE_CLIENT_ID ||
      !credsData.APPTILE_SUPABASE_CLIENT_SECRET
    ) {
      throw new Error("Missing required credentials for token refresh");
    }

    // Prepare form data for token refresh
    const formData = new URLSearchParams();
    formData.append("grant_type", "refresh_token");
    formData.append("refresh_token", credsData.SUPABASE_REFRESH_TOKEN);

    // Call Supabase OAuth endpoint to refresh the token
    const tokenResponse = await fetch(
      "https://api.supabase.com/v1/oauth/token",
      {
        method: "POST",
        headers: {
          "Content-Type": "application/x-www-form-urlencoded",
          Authorization: `Basic ${Buffer.from(
            `${credsData.APPTILE_SUPABASE_CLIENT_ID}:${credsData.APPTILE_SUPABASE_CLIENT_SECRET}`
          ).toString("base64")}`,
        },
        body: formData.toString(),
      }
    );

    if (!tokenResponse.ok) {
      throw new Error(`Token refresh failed: ${tokenResponse.statusText}`);
    }

    const tokenData = await tokenResponse.json();

    // Update the credentials in Apptile backend
    const updatedCreds = {
      APPTILE_SUPABASE_CLIENT_ID: credsData.APPTILE_SUPABASE_CLIENT_ID,
      APPTILE_SUPABASE_CLIENT_SECRET: credsData.APPTILE_SUPABASE_CLIENT_SECRET,
      SUPABASE_ACCESS_TOKEN: tokenData.access_token,
      SUPABASE_PROJECT_REF: credsData.SUPABASE_PROJECT_REF,
      SUPABASE_REFRESH_TOKEN: tokenData.refresh_token,
      SUPABASE_ANON_KEY: credsData.SUPABASE_ANON_KEY,
      ACCESS_TOKEN_EXPIRES_IN: tokenData.expires_in,
    };

    const updateResponse = await fetch(
      `${
        apptileConfig.APPTILE_BACKEND_URL
      }/${getMidUrlIntegrationsValue()}/${appId}/appIntegrations/supabase/credentials?isPlatformType=true`,
      {
        method: "POST",
        headers: { ...header.headers, "Content-Type": "application/json" },
        body: JSON.stringify(updatedCreds),
      }
    );

    if (!updateResponse.ok) {
      throw new Error(
        `Failed to update credentials: ${updateResponse.statusText}`
      );
    }

    res.status(200).json({
      message: "Supabase token refreshed successfully",
      expires_in: tokenData.expires_in,
      access_token: tokenData.access_token,
    });
  } catch (error: any) {
    console.error("Failed to refresh Supabase token:", error);
    res
      .status(500)
      .json({ error: error.message || "Failed to refresh Supabase token" });
  }
  // res.status(404);
  // res.end();
});

router.post(
  "/workspaces/:workspaceId/clone-open",
  jsonParser,
  async (req, res, next) => {
    try {
      const workspace = parseInt(req.params.workspaceId);
      // TODO(add types to manifest and appIntegrations)
      const manifest = req.body.manifest;
      const apptileConfig = req.body.apptileConfig;
      await createAppFolder(workspace, manifest, apptileConfig);

      const appRoot = await getAppLocation(workspace, manifest.uuid);
      insertOrUpdateOpenapp(String(workspace), manifest.uuid, appRoot);

      res.status(201).json({
        message: "App clone and open successful!",
      });
    } catch (err) {
      console.error("Failed to clone app", err);
      res.status(400).json({
        err,
      });
    }
  }
);

// Api to get the file tree
router.get("/:appId/file-tree/:type/:name", async (req, res, next) => {
  try {
    const app = await getOpenApp(req.params.appId);
    if (!app) {
      res.status(404).json({
        message: "App not found",
      });
      return;
    }
    const appRoot = app.repoPath;

    if (!req.params.type) {
      res.status(400).json({
        message: "Type is required",
      });
      return;
    }

    if (!req.params.name) {
      res.status(400).json({
        message: "Name is required",
      });
      return;
    }

    if (!["plugins", "navigators"].includes(req.params.type)) {
      res.status(400).json({
        message: "Type must be plugins or navigators",
      });
      return;
    }

    const folderPath = `${appRoot}/remoteCode/${req.params.type}/${req.params.name}/source`;
    try {
      await fsPromises.access(folderPath, constants.F_OK);
    } catch (err) {
      res.status(404).json({
        message: "Folder not found",
      });
      return;
    }

    const fileTree = await getFileTree(folderPath);
    res.json(fileTree);
  } catch (err) {
    console.error("Failed to get file tree", err);
    res.status(400).json({
      err,
    });
  }
});

// Api to get the file content
router.get("/:appId/file-content/:type/:name/:path", async (req, res, next) => {
  try {
    const app = await getOpenApp(req.params.appId);
    if (!app) {
      res.status(404).json({
        message: "App not found",
      });
      return;
    }
    const appRoot = app.repoPath;

    if (!req.params.type) {
      res.status(400).json({
        message: "Type is required",
      });
      return;
    }

    if (!req.params.name) {
      res.status(400).json({
        message: "Name is required",
      });
      return;
    }

    if (!["plugins", "navigators"].includes(req.params.type)) {
      res.status(400).json({
        message: "Type must be plugins or navigators",
      });
      return;
    }

    if (!req.params.path) {
      res.status(400).json({
        message: "Path is required",
      });
      return;
    }

    const filePath = `${appRoot}/remoteCode/${req.params.type}/${req.params.name}/source/${req.params.path}`;
    try {
      await fsPromises.access(filePath, constants.F_OK);
    } catch (err) {
      res.status(404).json({
        message: "File not found",
      });
      return;
    }

    const fileContent = await fsPromises.readFile(filePath, "utf-8");
    res.json(fileContent);
  } catch (err) {
    console.error("Failed to get file tree", err);
    res.status(400).json({
      err,
    });
  }
});

// Api to manage source files and folders creation and deletion
router.post("/:appId/manage-source", jsonParser, async (req, res, next) => {
  try {
    const appId = req.params.appId;
    const app = await getOpenApp(appId);
    if (!app) {
      res.status(404).json({
        message: "App not found",
      });
      return;
    }
    const appRoot = app.repoPath;
    const { itemType, itemName, relativePath, isFolder, action } = req.body;

    // Basic validation for required fields
    if (
      !itemType ||
      !itemName ||
      !relativePath ||
      typeof isFolder !== "boolean" ||
      !action
    ) {
      res.status(400).json({
        error:
          "Missing required parameters: itemType, itemName, relativePath, isFolder, action.",
      });
      return;
    }
    if (typeof isFolder !== "boolean") {
      res.status(400).json({ error: "isFolder must be a boolean." });
      return;
    }
    if (!["plugins", "navigators"].includes(itemType)) {
      res
        .status(400)
        .json({ error: "Invalid itemType. Must be 'plugin' or 'navigator'." });
      return;
    }
    if (!["create", "delete"].includes(action)) {
      res
        .status(400)
        .json({ error: "Invalid action. Must be 'create' or 'delete'." });
      return;
    }

    const result = await manageSourceFileOrFolder(
      {
        itemType,
        itemName,
        relativePath,
        isFolder,
        action,
      },
      `${appRoot}/remoteCode/${itemType}/${itemName}/source`
    );

    res.status(200).json(result);
  } catch (error: any) {
    console.error(
      `Failed to ${req.body.action} ${req.body.isFolder ? "folder" : "file"} '${req.body.relativePath}' in ${req.body.itemType} '${req.body.itemName}'`,
      error
    );
    // Send the specific error message from the controller, or a generic one
    res.status(400).json({
      error:
        error.message ||
        "An unexpected error occurred while managing the source item.",
    });
  }
});

// Api to update source files
router.post("/:appId/update-source", jsonParser, async (req, res, next) => {
  try {
    const appId = req.params.appId;
    const openedApp = await getOpenApp(appId);
    if (!openedApp) {
      res.status(404).json({
        message: "App not found. Please open the app first."
      });
      return;
    }
    const appRoot = openedApp.repoPath;
    const { itemType, itemName, relativePath, content } = req.body;
    if (!itemType || !itemName || !relativePath || !content) {
      res.status(400).json({
        error:
          "Missing required parameters: itemType, itemName, relativePath, content.",
      });
      return;
    }
    if(!["plugins", "navigators"].includes(itemType)) {
      res.status(400).json({
        error:
          "Invalid itemType. Must be 'plugins' or 'navigators'.",
      });
      return;
    }
    const sourceFilePath = path.resolve(
      appRoot,
      `remoteCode/${itemType}/${itemName}/source/${relativePath}`
    );
    await fsPromises.writeFile(sourceFilePath, content);
    res.status(200).json({
      message: "Source updated successfully",
    });
  }
  catch (error: any) {
    console.error("Failed to update source", error);
    res.status(400).json({
      error:
        error.message ||
        "An unexpected error occurred while updating the source item.",
    });
  }
})

export default router;
