"use strict"
import plugins from "@/plugins"
import path from "path"
import { baseDir, isDev, publicDir, sourceDir, templateDir } from "@/util"
import { validateJwt, validateSession } from "./auth"
import Hapi, { Server } from "@hapi/hapi"
import { Sequelize } from "sequelize"
import { Req, Res } from "#/global"
import { sequelize } from "./plugins/sequelize"
import fs from "fs-extra"
import { Boom } from "@hapi/boom"
// const Hapi = require("@hapi/hapi");
// const HapiSwagger = require("hapi-swagger");
// const HapiSwagger = require("hapi-swaggered-ui"); // swagger v2版本

const pugPluginAlias = require("pug-alias")

const run = async (): Promise<Server> => {
    const server = Hapi.server({
        port: 3388,
        host: "0.0.0.0",
        debug: isDev ? {
            request: ['error']
        } : false
    })
    server.events.on('request', (request, event, tags) => {
        if (tags.error) {
            loggerSite.error(request.path, "\n", event);
        } else {
            loggerSite.info(request.path, "\n", event);
        }
    });
    server.events.on('log', (event, tags) => {
        if (tags.error) {
            loggerSite.error(event);
        } else {
            loggerSite.info(event);
        }
    });
    await server.register([
        {
            plugin: require('hapi-sequelizejs'),
            options: [
                {
                    name: "data", // identifier
                    models: isDev ? ["source/models/**/*.ts"] : ["dist/models/**/*.js"], // paths/globs to model files
                    //   ignoredModels: [__dirname + "/server/models/**/*.js"], // OPTIONAL: paths/globs to ignore files
                    sequelize: sequelize, // sequelize instance
                    sync: true, // sync models - default false
                    forceSync: false, // force sync (drops tables) - default false
                },
            ],
        },
    ])

    //===== JWT ===== Start
    // await server.register(require("hapi-auth-jwt2"));
    // server.auth.strategy("jwt", "jwt", {
    //   key: process.env.KEY, // Never Share your secret key
    //   validate: validateJwt, // validate function defined above
    //   verifyOptions: { algorithms: ["HS256"] },
    // });
    //===== JWT =====  End
    //===== session ===== Start
    // https://hapi.dev/module/cookie/api?v=11.0.2
    await server.register(require("@hapi/cookie"))
    server.auth.strategy("session", "cookie", {
        cookie: {
            ttl: 1000 * 60 * 60 * 24,
            path: "/", // 测试退出时set-cookie失效,加上这个好了
            name: "sid", //cookie的名字
            password: process.env.KEY,
            isSecure: false, // false: 允许 Cookie 通过不安全的连接传输,这会使其受到攻击
            isHttpOnly: true,
            clearInvalid: false,
            strictHeader: true
        },
        redirectTo(request: Req) {
            if (request.path.startsWith("/api")) {
                return false
            }
            return "/login"
        },
        appendNext: true,
        validate: validateSession,
    })
    server.auth.default("session")
    //===== session =====  End

    await server.register(plugins as any)

    /**
     * 模板引擎
     */
    // https://hapi.dev/module/vision/api/?v=6.1.0
    await server.register(require("@hapi/vision"))
    const Pug = require("pug")
    Pug.filters.public = function (text, options) {
        const p = path.resolve(publicDir, text)
        if (fs.pathExistsSync(p)) {
            return fs.readFileSync(p, "utf-8")
        }
        return ""
    };
    server.views({
        engines: {
            ejs: require("ejs"),
            pug: Pug,
        },
        isCached: process.env.NODE_ENV === "development" ? false : true,
        compileMode: "sync", // ejs
        relativeTo: baseDir,
        layout: false, // ejs
        layoutPath: path.resolve(templateDir, "layout"), // ejs
        path: "template",
        // pug
        compileOptions: {
            // By default Pug uses relative paths (e.g. ../root.pug), when using absolute paths (e.g. include /root.pug), basedir is prepended.
            // https://pugjs.org/language/includes.html
            basedir: templateDir,
            plugins: [
                pugPluginAlias({
                    // as Function
                    "@": fn => fn.replace(/^@/, "template"),
                    "@hxpath": fn => fn.replace(/^@hxpath/, "template/htmx/path"),
                    "@views": fn => fn.replace(/^@views/, "template/views"),
                }),
            ],
        },
    })

    // http://localhost:3000/documentation
    await server.register([
        {
            plugin: require("hapi-swagger"),
            options: {
                documentationPath: "/doc",
                info: {
                    title: "Dream 文档",
                    version: "1.0.0",
                },
                grouping: "tags",
                tags: [
                    {
                        name: "sum",
                        description: "working with maths",
                        externalDocs: {
                            description: "Find out more",
                            url: "http://example.org",
                        },
                    },
                    {
                        name: "store",
                        description: "storing data",
                        externalDocs: {
                            description: "Find out more",
                            url: "http://example.org",
                        },
                    },
                ],
            },
        },
    ])

    server.ext('onPreResponse', (request, h) => {
        const { response } = request
        // 如果状态码为403,则调用自定义错误处理程序
        if (response instanceof Boom && (response.output.statusCode < 200 || response.output.statusCode > 299)) {
            return h.view('403.pug', { error: response})
        }
        return h.continue;
    });

    await server.start()
    logger.trace("Server running on %s", server.info.uri.replace("0.0.0.0", "localhost"))
    return server
}

process.on("unhandledRejection", err => {
    console.log("unhandledRejection:", err)
    process.exit(1)
})

export { run }