const path = require("path");
const MemoryFS = require("memory-fs")
const fs = require("fs")
const fetch = require("node-fetch")

const getDate = () => new Date().toISOString();



module.exports = async function executeServer({api, log, actions}) {
  const Koa = require("koa");
  const http = require("http");
  const fileSystem = new MemoryFS()

  const app = new Koa()
  const root = require.resolve("../../package.json").replace("package.json", "www");

  const {data: url} = await api.website.getEditorJS();
  const editorSrc = await fetch(url).then(r => r.text())
  const p = path.join(__dirname, '../../app/weblium-editor.js')
  try {
    fs.unlinkSync(p)
  } catch (e) {
    // noop
  }
  fs.writeFileSync(p, editorSrc)

  await require("./start-webpack")(fileSystem);

  app.use(async function(cntx) {
    log.info(getDate(), `GET ${cntx.path}`)
    if(api && cntx.path == "/editor.js") {
      const {data: url} = await api.website.getEditorJS();
      cntx.set("content-type", "application/json");
      cntx.body = await fetch(url).then(r => r.text());
      return;
    }
    const fname = path.join(root, !cntx.path || cntx.path == "/"? "index.html": cntx.path)
    if(!fileSystem.existsSync(fname)) return log.error(getDate(), cntx.status = 404);
    const {ext} = path.parse(fname)
    const contentType = ({
      ".html": "text/html", ".htm": "text/html",
      ".js": "application/javascript",
      ".png": "image/png",
      ".jpg": "image/jpeg", ".jpeg": "image/jpeg",
      ".css": "text/css",
      ".svg": "image/svg+xml",
    })[ext];
    contentType && cntx.set("content-type", contentType)
    cntx.body = fileSystem.createReadStream(fname);
  });

  let port = 0;
  process.env.NODE_ENV == "dev-webpack" && (port = 3013)

  const server = http.createServer(app.callback());

  return await new Promise((resolve, reject) => {
    server.listen(port);
    server.on('listening', function() {
      port = server.address().port;
      log.info(getDate(), `server started on port ${port}`);

      const io = require("socket.io")(server, {});

      require("open")(`http://localhost:${port}`);

      io.on("error", reject);
      let connections = 0
      io.on("connection", socket => {
        connections++;
        executeServer.notify = data => socket.emit("api", data)
        socket.on("disconnect", function() {
          connections--;
          log.info(getDate(), "disconnected");
          if(process.env.NODE_ENV != "dev-webpack") {
            setTimeout(() => {
              if(connections) return;
              server.close();
              resolve();
            }, 2000)
          }
        });
        socket.on("api", ({messageId, action, data}) => {
          const sendError = error => {
            log.error(getDate(), `${action}:${messageId} => `, error)
            socket.emit("api", {messageId, error: {message: Error.message || "" + error}})
          };
          log.info(getDate(), `${action}:${messageId}(${data})`);

          try {
            const f = actions[action]
            if(!f) throw new Error(`invalid action ${action}`);
            const res = f(data);
            if(res && res.then) {
              res.then(data => {
                log.info(getDate(), `${action}:${messageId} => ${data}`);
                socket.emit("api", {messageId, data})
              }).catch(sendError)
            } else {
              log.info(getDate(), `${action}:${messageId} => ${data}`);
              socket.emit("api", {messageId, data: res});
            }
          } catch(error) {
            sendError(error);
          }
        })
      });
    });
  })
}
