import express, {Request} from 'express';
import path from 'path';
import bodyParser from 'body-parser'
import {readdir, stat, mkdir, readFile, writeFile, rm} from 'node:fs/promises';
import {sha256} from 'js-sha256';

import {compileMultipleNavCreators} from '../controllers/compiler';
import {sendLog} from '../websocket';
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 navroot = path.resolve(openedApp.repoPath, 'remoteCode/navigators');
      const contents = await readdir(
        navroot,
        {  withFileTypes: true }
      );
      const dirs = contents.filter(it => it.isDirectory()).map(it => it.name);
      res.json(dirs);
    } else {
      res.status(404);
      res.end();
    }
  } catch (err) {
    next(err);
  }
});

router.get("/:appid/:assetname/source", async (req, res) => {
  try {
    const appId = req.params.appid;
    const navName = req.params.assetname;
    const openedApp = getOpenApp(appId);
    if (openedApp) {
      const navFile = path.resolve(openedApp.repoPath, `remoteCode/navigators/${navName}/source/index.jsx`);
      sendLog(`component source file path: ${navFile}`);
      res.sendFile(navFile);
    } else {
      sendLog(`Link not found, cannot send source for: ${navName}`);
      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 {
      res.status(500);
      res.send("in /:name/source a server error occured. Check server logs.");
    }
  }
});

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

router.delete("/:appid/:assetname", async (req, res) => {
  try {
    const appId = req.params.appid;
    const navName = req.params.assetname;
    const openedApp = getOpenApp(appId);
    if (openedApp) {
      const dirPath = path.resolve(openedApp.repoPath, 'remoteCode/navigators', navName);
      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);
    res.status(500)
    res.end()
  }
});


// TODO(gaurav): add ensurefolders middleware
router.get('/:appid/getall', async (req: Request<{appid: string;}, {}, {}, {navigators: string;}>, res) => {
  // check if compiled files exist
  const appId = req.params.appid;
  const navs = req.query.navigators?.split(',').filter(it => !!it) ?? [];
  const openedApp = getOpenApp(appId);
  console.log(`Getting all navigators for: ${appId} ${req.query.navigators}`);
  if (openedApp && navs.length > 0) {
    const compiledDirectoryName = sha256('navigators_' + navs.join('_')).toString();
    try {
      const bundlePath = path.resolve(openedApp.repoPath, 'remoteCode/generated', compiledDirectoryName, 'dist/bundle.js');
      await stat(bundlePath);
      res.sendFile(bundlePath);
    } catch (err: any) {
      if (err?.code === 'ENOENT') {
        const compileResult = await compileMultipleNavCreators(openedApp.repoPath, navs, compiledDirectoryName);
        if (!compileResult) {
          const bundlePath = path.resolve(openedApp.repoPath, 'remoteCode/plugins', compiledDirectoryName, 'dist/bundle.js');
          res.sendFile(bundlePath)
        } else {
          res.send("Compilation failed");
        }
      } else {
        console.error(err);
        res.status(500);
        res.end();
      }
    }
  } else {
    sendLog(`Cannot send bundle for ${appId} as there are no navigators queried for ${req.query.navigators}`);
    res.status(404);
    res.end();
  }
});

router.post("/:appid/create", jsonParser, async (req, res) => {
  console.log("creating new navigator");
  const appId = req.params.appid;
  const openedApp = getOpenApp(appId);
  if (openedApp) {
    try {
      const name = req.body.navRegistryName.toLowerCase();
      const navRoot = path.resolve(openedApp.repoPath, `remoteCode/navigators`, name)
      const sourceFolder = path.resolve(navRoot, "source");
      await mkdir(sourceFolder, {recursive: true})
      const navTemplateFile = path.resolve(__dirname, "../templates/tabNavigator.jsx");
      const basicTabNav = await readFile(navTemplateFile, {encoding: "utf-8"})
      await writeFile(path.resolve(sourceFolder, "index.jsx"), basicTabNav);
      await mkdir(path.resolve(navRoot, "dist"), {recursive: true});
      res.status(200);
      res.end();
    } catch (err) {
      console.error("in POST /create", err);
      res.status(500);
      res.end();
    }
  } else {
    console.log("Link doesn't exist. Ignoring");
    res.status(400);
    res.end();
  }
});

router.post("/:appid/compileall", jsonParser, async (req, res) => {
  try {
    const appId = req.params.appid;
    const openedApp = getOpenApp(appId);
    if (openedApp) {
      let plugins = req.body.plugins
      if (!plugins) {
        const dirs = await readdir(path.resolve(openedApp.repoPath, `remoteCode/navigators`), {withFileTypes: true})
        plugins = dirs.filter(it => it.isDirectory()).map(it => it.name);
      }
      // sendLiveUpdateRequest(appid)
      console.log("Recompiling navigators: " + plugins.join(","))
      const compiledDirectoryName = sha256('navigators_' + plugins.join('_')).toString();
      const result = await compileMultipleNavCreators(openedApp.repoPath, plugins, compiledDirectoryName)
      res.send(result);
    } else {
      console.error("Link not found. Ignoring");
      res.status(500);
      res.end();
    }
  } catch (err) {
    console.error("in /compileall", err);
    if ((err as any)?.message && (err as any)?.errors) {
      res.json((err as any));
    } else {
      res.send("in /compileall a server error occured. Check server logs.");
    }
  }
});

export default router;
