var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
import http from "node:http";
import { URL } from "node:url";
import querystring from "node:querystring";
import Router from "./router.js";
import Tpl from "./template.js";
export { Router, Tpl };
export default class Flummpress {
    constructor() {
        this.router = new Router();
        this.tpl = new Tpl();
        this.middleware = [];
    }
    use(plugin) {
        if (plugin instanceof Router)
            this.router.use(plugin);
        else if (plugin instanceof Tpl)
            this.tpl = plugin;
        else if (typeof plugin === "function")
            this.middleware.push(plugin);
        return this;
    }
    processPipeline(handlers, req, res) {
        return __awaiter(this, void 0, void 0, function* () {
            for (const handler of handlers) {
                if (typeof handler !== "function")
                    throw new TypeError(`Handler is not a function: ${handler}`);
                let nextCalled = false;
                yield handler(req, res, () => nextCalled = true);
                if (!nextCalled || res.writableEnded)
                    return;
            }
        });
    }
    listen(...args) {
        this.server = http.createServer((request, response) => __awaiter(this, void 0, void 0, function* () {
            var _a, _b;
            const req = this.parseRequest(request);
            const res = this.createResponse(response);
            const start = process.hrtime();
            try {
                yield this.processPipeline(this.middleware, req, res);
                const route = this.router.getRoute(req.url.pathname, req.method);
                if (route) {
                    const handler = route.methods[(_a = req.method) === null || _a === void 0 ? void 0 : _a.toLowerCase()];
                    req.params = ((_b = req.url.pathname.match(new RegExp(route.path))) === null || _b === void 0 ? void 0 : _b.groups) || {};
                    req.post = yield this.readBody(req);
                    yield this.processPipeline(handler, req, res);
                }
                else {
                    res.writeHead(404).end("404 - Not Found");
                }
            }
            catch (err) {
                console.error(err);
                res.writeHead(500).end("500 - Internal Server Error");
            }
            console.log([
                `[${new Date().toISOString()}]`,
                `${(process.hrtime(start)[1] / 1e6).toFixed(2)}ms`,
                `${req.method} ${res.statusCode}`,
                req.url.pathname,
            ].join(" | "));
        })).listen(...args);
        return this;
    }
    parseRequest(request) {
        const url = new URL(request.url.replace(/(?!^.)(\/+)?$/, ""), "http://localhost");
        const req = request;
        req.url = {
            pathname: url.pathname,
            split: url.pathname.split("/").slice(1),
            searchParams: url.searchParams,
            qs: Object.fromEntries(url.searchParams.entries()),
        };
        req.cookies = {};
        if (req.headers.cookie) {
            req.headers.cookie.split("; ").forEach(cookie => {
                const [key, value] = cookie.split("=");
                req.cookies[key] = decodeURIComponent(value);
            });
        }
        return req;
    }
    readBody(req) {
        return __awaiter(this, void 0, void 0, function* () {
            return new Promise((resolve, reject) => {
                let body = "";
                req.on("data", (chunk) => {
                    body += chunk;
                });
                req.on("end", () => {
                    try {
                        resolve(req.headers["content-type"] === "application/json"
                            ? JSON.parse(body)
                            : querystring.parse(body));
                    }
                    catch (err) {
                        reject(err);
                    }
                });
                req.on("error", reject);
            });
        });
    }
    createResponse(response) {
        const res = response;
        res.reply = ({ code = 200, type = "text/html", body }) => {
            res.writeHead(code, { "Content-Type": `${type}; charset=utf-8` });
            res.end(body);
        };
        res.json = (body, code = 200) => {
            res.reply({ code, type: "application/json", body: JSON.stringify(body) });
        };
        res.html = (body, code = 200) => {
            res.reply({ code, type: "text/html", body });
        };
        res.redirect = (target, code = 302) => {
            res.writeHead(code, { Location: encodeURI(target) });
            res.end();
        };
        return res;
    }
}