const fs = require('fs');
const Koa = require('koa');
const path = require('path');
const sirv = require('sirv');
const Proxy = require('koa-proxies')
// const Router = require('@koa/router');
const loading = require('loading-cli');
const koaConnect = require('koa-connect')
const { actionRecord } = require('./private');
const { replaceFalseString } = require('./utils/func')
const { router: Router, routes } = require('./utils/initRoutes');

module.exports = async ({ cwd, env, argv, rawArgv, startTime = new Date().getTime() }) => {
   // console.log(cwd, env, argv, rawArgv);
   const load = loading("启动 React client").start()
   const conf = replaceFalseString(
      require(path.join(cwd, '/config.cjs'))
   )

   const isProduction = conf.env != 'development';
   const base = '/';
   const exportEnv = Object.entries(process.env)
      // 整合环境变量传递到前端 window.env 中
      .reduce((acc, [key, value]) => {
         if (key.startsWith(conf?.vite?.envPrefix ?? 'FS_')) {
            acc[key] = value;
         }
         return acc;
      }, {});

   // Cached production assets
   let templateHtml = '';
   let ssrManifest;

   // Create Koa server
   const app = new Koa();
   const router_ = new Router();

   let vite

   if (conf.proxy) {
      for (let p of conf.proxy) {
         app.use(Proxy(p.path, {
            ...p
         }))
      }
   }

   if (isProduction) {
      templateHtml = fs.readFileSync(path.join(cwd, '/dist/client/index.html'), 'utf-8')
      ssrManifest = fs.readFileSync(path.join(cwd, '/dist/client/.vite/ssr-manifest.json'), 'utf-8')
      app.use(koaConnect(sirv(path.join(cwd, '/dist/client'), {
         single: true,
         extensions: [],
      })))
   } else {
      const { createServer } = await import('vite')
      const serverConf = await require('./vite.config')({ cwd, env, userConf: conf?.vite, target: 'dev' })
      vite = await createServer(serverConf);
      app.use(koaConnect(vite.middlewares));
   }

   app.use(async (ctx, next) => {
      try {
         // ctx.logger = logger4js_;
         ctx.config = conf
         await next();
      } catch (error) {
         // ctx.logger.error(error);
         ctx.status = error.status || 500;
         ctx.body = {
            message:
               typeof error == 'string' ?
                  error : error?.message ?? '网络错误，请稍候重试'
         };
      }
   })

   app.use(  // 加载 自定义路由
      routes(app, router_, conf, path.join(cwd, '/server/routes'), load)
   )

   // Middleware for rendering HTML
   app.use(async (ctx) => {
      try {
         const url = ctx.originalUrl.replace(base, '');
         let template;
         let render;
         if (isProduction) {
            template = templateHtml;
            render = (await import(path.join('file:///', cwd, '/dist/server/server.mjs'))).render
         } else {
            // Always read fresh template in development
            template = fs.readFileSync(path.join(cwd, 'index.html'), 'utf-8');
            template = await vite.transformIndexHtml(url, template);
            render = (await vite.ssrLoadModule(path.join(cwd, '/client/entry/server.jsx'))).render;
         }

         const rendered = await render(url, ssrManifest);

         const html = template
            .replace(`<!--app-head-->`, rendered?.head ?? '')
            .replace(`<!--app-ico-->`, conf?.favicon ?? '') // production 会在构建的时候即替换
            .replace(`<!--app-title-->`, conf?.title ?? '')
            .replace(`<!--app-script-->`, conf?.scripts?.map(sc => `<script src=${sc}></script>`).join('\n') ?? '') // production 会在构建的时候即替换
            .replace(`<!--app-html-->`, rendered.html ?? '')
            .replace(`<!--app-env-->`, `
                <script>
                    window.env=${JSON.stringify(exportEnv)}
                </script>
            `)

         ctx.status = 200;
         ctx.type = 'text/html';
         ctx.body = html;
      } catch (e) {
         vite?.ssrFixStacktrace(e)
         console.error(e.stack);
         ctx.status = 500;
         ctx.body = e.stack;
      }
   });


   app.listen(parseInt(conf.port), () => {
      const endTime = new Date().getTime();
      load.succeed(`客户端已启动于 http://127.0.0.1:${conf.port} [${((endTime - startTime) / 1000).toFixed(2)}s.]`);

      actionRecord({
         action: 'start',
         spend: endTime - startTime,
         env: conf?.env
      })
   });
}
