import express, {Request} from 'express';
import path from 'path';
import {readdir, rm, mkdir, cp, readFile, writeFile} from "node:fs/promises";
import bodyParser from 'body-parser'
import { sendLog } from '../websocket';
import { compilePluginBundle, getPluginBundle } from '../controllers/workspace';
import { getOpenApp } from '../database/cliconfig';
export const jsonParser = bodyParser.json({limit: '50mb'});

const router = express.Router();

router.get('/:appid/list', async (req, res, next) => {
  try {
    const appId = req.params.appid;
    const openedApp = getOpenApp(appId);
    if (openedApp) {
      const pluginsRoot = path.resolve(openedApp.repoPath, 'remoteCode/plugins');
      try {
        const contents = await readdir(
          pluginsRoot,
          {  withFileTypes: true }
        );
        const dirs = contents.filter(it => it.isDirectory()).map(it => it.name);
        res.json(dirs);
      } catch (err: any) {
        if (err?.code === 'ENOENT') {
          res.json([]);
        } else {
          console.error("Unhandelable error", err);
          next(err);
        }
      }
    } else {
      res.status(404);
      res.end();
    }
  } catch (err) {
    next(err);
  }
});

router.post("/:appid/create", jsonParser, async (req, res, next) => {
  const appId = req.params.appid;
  const openedApp = getOpenApp(appId);
  if (openedApp) {
    try {
      const body = {
        __LABEL_PREFIX__: req.body.labelPrefix,
        __LISTING_NAME__: req.body.listingName,
        __DISPLAY_DESCRIPTION__: req.body.displayDescription,
        __LISTING_ICON__: req.body.listingIcon,
        __PLUGIN_REGISTRY_NAME__: req.body.pluginRegistryName,
        __PLUGIN_NAME__: req.body.listingName
      };
      const name = req.body.pluginRegistryName.toLowerCase();
      const pluginRoot = path.resolve(openedApp.repoPath, `remoteCode/plugins`, name)
      try {
        await mkdir(path.resolve(pluginRoot, "source"), {recursive: true})
      } catch (err: any) {
        if (err?.code !== 'EEXIST') {
          console.error("Unhandelable error", err);
          next(err);
        } 
      }
      const widgetFile = path.resolve(__dirname, "../templates/widget.jsx");
      const p1 = readFile(widgetFile, { encoding: "utf-8" })
        .then(basicWidgetConfig => {
          basicWidgetConfig = basicWidgetConfig.replace(/__LABEL_PREFIX__/g, body.__LABEL_PREFIX__)
            .replace(/__LISTING_NAME__/g, body.__LISTING_NAME__)
            .replace(/__DISPLAY_DESCRIPTION__/g, body.__DISPLAY_DESCRIPTION__)
            .replace(/__LISTING_ICON__/g, body.__LISTING_ICON__)
            .replace(/__PLUGIN_REGISTRY_NAME__/g, body.__PLUGIN_REGISTRY_NAME__)
            .replace(/__PLUGIN_NAME__/g, appId + '/' + body.__PLUGIN_NAME__);
          return writeFile(path.resolve(pluginRoot, "source/widget.jsx"),
            basicWidgetConfig)
        });
      const componentFile = path.resolve(__dirname, "../templates/component.jsx");
      const p2 = cp(
        componentFile,
        path.resolve(pluginRoot, "source/component.jsx")
      );
      const p3 = await mkdir(path.resolve(pluginRoot, "dist"), {recursive: true});
      await Promise.all([p1, p2, p3]);
      res.status(200);
      res.end();
    } catch (err) {
      console.error("in POST /create", err);
      next(err);
    }
  } else {
    console.log("Link doesn't exist. Ignoring");
    res.status(400);
    res.end();
  }
});

router.get('/:appid/getall', async (req: Request<{appid: string;}, {}, {}, {plugins: string;}>, res, next) => {
  // check if compiled files exist
  const appId = req.params.appid;
  try {
    const pluginBundleFilePath = await getPluginBundle(appId);
    res.sendFile(pluginBundleFilePath);
  } catch (err) {
    next(err);
  }
});

router.post("/:appId/compileall", jsonParser, async (req, res, next) => {
  try {
    const appId = req.params.appId;
    const result = await compilePluginBundle(appId);
    res.send(result);
  } catch (err) {
    console.error("in /compileall", err);
    if ((err as any)?.message && (err as any)?.errors) {
      res.json((err as any));
    } else {
      next(err);
    }
  }
});

router.get("/:appid/:assetname/source", async (req, res) => {
  try {
    const appId = req.params.appid;
    const pluginName = req.params.assetname;
    const openedApp = getOpenApp(appId);
    if (openedApp) {
      const remoteCode = path.resolve(openedApp.repoPath, `remoteCode/plugins/${pluginName}/source/component.jsx`);
      console.log(`Looking for source file: ${remoteCode}`)
      res.sendFile(remoteCode);
    } else {
      res.status(404);
      res.end();
    }
  } catch (err: any) {
    sendLog("in GET /:name/source" + err?.message);
    if ((err as any)?.message && (err as any)?.errors) {
      res.json(err);
    } else {
      console.error("Open the app first")
      res.status(500);
      res.send("in /:name/source a server error occured. Check server logs.");
    }
  }
  // return the component source code
});

router.post("/:appid/:assetname/source", jsonParser, async (req, res, next) => {
  try {
    const appId = req.params.appid;
    const pluginName = req.params.assetname;
    const openedApp = getOpenApp(appId);
    const code = req.body.code;
    if (openedApp) {
      const sourceFilePath = path.resolve(openedApp.repoPath, 
                                          `remoteCode/plugins/${pluginName}/source/component.jsx`)
      const results = await writeFile(sourceFilePath, code);
      res.json(results);
    } else {
      sendLog(`Link not found, cannot write source for: ${pluginName}`);
      res.status(404);
      res.end();
    }
  } catch (err) {
    console.error("in POST /:name/source", err);
    next(err);
  }
});


// TODO(gaurav): add ensurefolders middleware
router.delete("/:appid/:assetname", async (req, res, next) => {
  try {
    const appId = req.params.appid;
    const pluginName = req.params.assetname;
    const openedApp = getOpenApp(appId);
    if (openedApp) { 
      const dirPath = path.resolve(openedApp.repoPath, 'remoteCode/plugins', pluginName);
      await rm(dirPath, { recursive: true, maxRetries: 4 });
      res.status(200);
      res.end();
    } else {
      res.status(404);
      res.end();
    }
  } catch (err) {
    console.error("in DELETE /:assetname", err);
    next(err);
  }
});

export default router;
