import fs from 'fs'
import path from 'path'
import { fileURLToPath } from 'url'
import express from 'express'
import serverStatic from 'serve-static'
import { createServer as createViteServer } from 'vite'
import ssrManifest from './dist/client/.vite/ssr-manifest.json' assert { type: "json" }
import *  as PRODserver from './dist/server/entry-server.js'
const isProd = process.env.NODE_ENV  === 'production'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
async function createServer() {
    const app = express()
    // 以中间件模式创建 Vite 应用，并将 appType 配置为 'custom'
    // 这将禁用 Vite 自身的 HTML 服务逻辑
    // 并让上级服务器接管控制
    const vite = await createViteServer({
        server: { middlewareMode: 'ssr' },
        appType: 'custom'
    })
    if(!isProd){
        // 使用 vite 的 Connect 实例作为中间件
        // 如果你使用了自己的 express 路由（express.Router()），你应该使用 router.use
        // 当服务器重启（例如用户修改了 vite.config.js 后），
        // `vite.middlewares` 仍将保持相同的引用
        // （带有 Vite 和插件注入的新的内部中间件堆栈）。
        // 即使在重新启动后，以下内容仍然有效。
        app.use(vite.middlewares)
    }else{
        app.use(serverStatic(path.resolve(__dirname,'dist/client'),{index:false}))
    }
    // 参考https://cn.vitejs.dev/guide/ssr.html
    app.use('*', async(req, res) => {
        const url = req.originalUrl
        let template
        let render
        let html
        try {
            if(!isProd){
                console.log("开启开发服务端渲染（路径=>源码）")
                 // 1. 读取 index.html
                template = fs.readFileSync(
                    path.resolve(__dirname, 'index.html'),
                    'utf-8',
                )
                // 2. 应用 Vite HTML 转换。这将会注入 Vite HMR 客户端，
                //    同时也会从 Vite 插件应用 HTML 转换。
                //    例如：@vitejs/plugin-react 中的 global preambles
                template = await vite.transformIndexHtml(url, template)

                // 3a. 加载服务器入口。vite.ssrLoadModule 将自动转换
                //    你的 ESM 源码使之可以在 Node.js 中运行！无需打包
                //    并提供类似 HMR 的根据情况随时失效。
                render  = (await vite.ssrLoadModule('/src/entry-server.ts')).render
                // 3b. 从 Vite 5.1 版本开始，你可以试用实验性的 createViteRuntime
                // API。
                // 这个 API 完全支持热更新（HMR），其工作原理与 ssrLoadModule 相似
                // 如果你想尝试更高级的用法，可以考虑在另一个线程，甚至是在另一台机器上，
                // 使用 ViteRuntime 类来创建运行环境。
                // const runtime = await vite.createViteRuntime(server)
                // const { render } = await runtime.executeEntrypoint('/src/entry-server.ts')
            }else{
                console.log("开启生产服务端渲染（路径=>dist）")
                template = fs.readFileSync(
                    path.resolve(__dirname, 'dist/client/index.html'),
                    'utf-8',
                )
                render  = PRODserver.render
            }
            // 4. 渲染应用的 HTML。这假设 entry-server.js 导出的 `render`
            //    函数调用了适当的 SSR 框架 API。
            //    例如 ReactDOMServer.renderToString()
            
            if(!isProd){
                const { appHtml, state } = await render(url)
                const { title,keywords, description } = state.meta
                // 5. 注入渲染后的应用程序 HTML 到模板中。
                html = template
                .replace(`<!--ssr-outlet-->`, appHtml)
                .replace('\'<!--vuex-state-->\'', JSON.stringify(state))
                .replace('<title>',`<title>${title}`)
                .replace('<meta name="keywords" content="">',`<meta name="keywords" content="${keywords}">`)
                .replace('<meta name="description" content="">',`<meta name="description" content="${description}">`)
               
            }else{
                const manifest =  ssrManifest
                const { appHtml, state,preloadLinks } = await render(url,manifest)
                const { title,keywords, description } = state.meta
                html = template
                .replace(`<!--preload-links-->`, preloadLinks)
                .replace(`<!--ssr-outlet-->`, appHtml)
                .replace('\'<!--vuex-state-->\'', JSON.stringify(state))
                .replace('<title>',`<title>${title}`)
                .replace('<meta name="keywords" content="">',`<meta name="keywords" content="${keywords}">`)
                .replace('<meta name="description" content="">',`<meta name="description" content="${description}">`)
            }
            // 6. 返回渲染后的 HTML。
            res.status(200).set({ 'Content-Type': 'text/html' }).end(html)
        } catch (e) {
            // 如果捕获到了一个错误，让 Vite 来修复该堆栈，这样它就可以映射回
            // 你的实际源码中。
            // vite.ssrFixStacktrace(e)
            // console.log(e)
            // next(e)
        }
    })
    app.listen(5173, () => {
        console.log("node server.js",isProd ? `运行 生产环境` : `运行 开发环境`)
    })
}
createServer()