import { UserIps } from "../../models/index.js";
import { fetchMeta } from "../../misc/fetch-meta.js";
import authenticate, { AuthenticationError } from "./authenticate.js";
import call from "./call.js";
import { ApiError } from "./error.js";
const userIpHistories = new Map();
setInterval(()=>{
    userIpHistories.clear();
}, 1000 * 60 * 60);
export default ((endpoint, ctx)=>new Promise((res)=>{
        const body = ctx.is("multipart/form-data") ? ctx.request.body : ctx.method === "GET" ? ctx.query : ctx.request.body;
        const reply = (x, y)=>{
            if (x == null) {
                ctx.status = 204;
            } else if (typeof x === "number" && y) {
                ctx.status = x;
                ctx.body = {
                    error: {
                        message: y.message,
                        code: y.code,
                        id: y.id,
                        kind: y.kind,
                        ...y.info ? {
                            info: y.info
                        } : {}
                    }
                };
            } else {
                // 文字列を返す場合は、JSON.stringify通さないとJSONと認識されない
                ctx.body = typeof x === "string" ? JSON.stringify(x) : x;
            }
            res();
        };
        // Authentication
        // for GET requests, do not even pass on the body parameter as it is considered unsafe
        authenticate(ctx.headers.authorization, ctx.method === "GET" ? null : body["i"]).then(([user, app])=>{
            // API invoking
            call(endpoint.name, user, app, body, ctx).then((res)=>{
                if (ctx.method === "GET" && endpoint.meta.cacheSec && !body["i"] && !user) {
                    ctx.set("Cache-Control", `public, max-age=${endpoint.meta.cacheSec}`);
                }
                reply(res);
            }).catch((e)=>{
                reply(e.httpStatusCode ? e.httpStatusCode : e.kind === "client" ? 400 : 500, e);
            });
            // Log IP
            if (user) {
                fetchMeta().then((meta)=>{
                    if (!meta.enableIpLogging) return;
                    const ip = ctx.ip;
                    const ips = userIpHistories.get(user.id);
                    if (ips == null || !ips.has(ip)) {
                        if (ips == null) {
                            userIpHistories.set(user.id, new Set([
                                ip
                            ]));
                        } else {
                            ips.add(ip);
                        }
                        try {
                            UserIps.createQueryBuilder().insert().values({
                                createdAt: new Date(),
                                userId: user.id,
                                ip: ip
                            }).orIgnore(true).execute();
                        } catch  {}
                    }
                });
            }
        }).catch((e)=>{
            if (e instanceof AuthenticationError) {
                ctx.response.status = 403;
                ctx.response.set("WWW-Authenticate", "Bearer");
                ctx.response.body = {
                    message: `Authentication failed: ${e.message}`,
                    code: "AUTHENTICATION_FAILED",
                    id: "b0a7f5f8-dc2f-4171-b91f-de88ad238e14",
                    kind: "client"
                };
                res();
            } else {
                reply(500, new ApiError());
            }
        });
    }));
