// Identity. Store your memories and mental belongings
// Copyright (C) 2024  SofĂ­a Aritz
// 
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
// 
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

import app from "./app.js"
import { ASSET_API_ENDPOINT, IDENTITY_API_LANDING_MESSAGE, LISTEN_PORT } from "./consts.js"
import { contentFromSigned, verifySignature } from "./m2m.js";
import { startAuth } from "./auth.js";
import { randomUUID } from "node:crypto";
import cors from "@fastify/cors";

let auth = await startAuth();

app.register(cors, {
    origin: true,
})

app.get("/", async () => {
    return IDENTITY_API_LANDING_MESSAGE;
});

app.put("/m2m/asset", {
    async handler(request, reply) {
        if (!verifySignature(request.body)) {
            reply.statusCode(401);
            return;
        }

        let body = JSON.parse(contentFromSigned(request.body));

        let user = await auth.findUserBySessionKey(body.session_key)
        user.assets.push(body.asset_id);

        await auth.updateUser(user.uid, user);
        app.log.info((await auth.findUserBySessionKey(body.session_key)).assets);
    },
});

app.post("/m2m/account", {
    async handler(request, reply) {
        if (!verifySignature(request.body)) {
            reply.statusCode(401);
            return;
        }

        let body = JSON.parse(contentFromSigned(request.body));

        let user = await auth.findUserBySessionKey(body.session_key)
        user.password = undefined;
        user.entries = undefined;

        return user;
    },
});

app.get("/asset/endpoint", {
    async handler() {
        return ASSET_API_ENDPOINT
    }
})

app.delete("/entry", {
    async handler(request, reply) {
        let jwt = request.headers["authorization"].replace("Bearer", "").trim();
        let { payload } = await auth.verifyJwt(jwt);

        let user = await auth.user(payload.uid);
        user.entries = user.entries.filter(v => v.id !== request.query.entry_id);

        await auth.updateUser(payload.uid, user);
    },
    schema: {
        headers: {
            type: "object",
            properties: {
                authorization: { type: "string" },
            },
            required: ["authorization"],
        },
        query: {
            type: "object",
            properties: {
                entry_id: { type: "string" },
            },
            required: ["entry_id"],
        },
    },
})

app.put("/entry", {
    async handler(request, reply) {
        let jwt = request.headers["authorization"].replace("Bearer", "").trim();
        let { payload } = await auth.verifyJwt(jwt);

        let user = await auth.user(payload.uid);
        request.body.entry.id = randomUUID().toString();
        user.entries = [request.body.entry, ...user.entries];

        await auth.updateUser(payload.uid, user);
    },
    schema: {
        headers: {
            type: "object",
            properties: {
                authorization: { type: "string" },
            },
            required: ["authorization"],
        },
        body: {
            type: "object",
            properties: {
                entry: { type: "object" },
            },
            required: ["entry"],
        },
    },
})

app.get("/entry/list", {
    async handler(request, reply) {
        if (request.query.offset < 0 || request.query.limit <= 0) {
            reply.status(400);
            return [];
        }

        let jwt = request.headers["authorization"].replace("Bearer", "").trim();
        let { payload } = await auth.verifyJwt(jwt);

        let user = await auth.user(payload.uid);
        return user.entries.slice(request.query.offset, request.query.offset + request.query.limit);
    },
    schema: {
        headers: {
            type: "object",
            properties: {
                authorization: { type: "string" },
            },
            required: ["authorization"],
        },
        query: {
            type: "object",
            properties: {
                limit: { type: "number" },
                offset: { type: "number" },
            },
            required: ["limit", "offset"],
        },
    },
})

app.post("/auth/heirs", {
    async handler(request, reply) {
        let jwt = request.headers["authorization"].replace("Bearer", "").trim();
        let { payload } = await auth.verifyJwt(jwt);

        if (payload.uid == null) {
            reply.status(401);
            return;
        }

        let user = await auth.user(payload.uid);
        user.heirs = request.body;

        await auth.updateUser(payload.uid, user);
    },
    schema: {
        headers: {
            type: "object",
            properties: {
                authorization: { type: "string" },
            },
            required: ["authorization"],
        },
        body: {
            type: "array",
        }
    }
})

app.get("/auth/genkey", {
    async handler(request) {
        let jwt = request.headers["authorization"].replace("Bearer", "").trim();
        let { payload } = await auth.verifyJwt(jwt);

        let session_key = await auth.createSessionKey(payload.uid);

        return {
            session_key,
        };
    },
    schema: {
        headers: {
            type: "object",
            properties: {
                authorization: { type: "string" },
            },
            required: ["authorization"],
        },
    },
});

app.get("/auth/account", {
    async handler(request, reply) {
        let jwt = request.headers["authorization"].replace("Bearer", "").trim();
        let { payload } = await auth.verifyJwt(jwt);

        if (payload.uid == null) {
            reply.status(401);
            return;
        }

        let user = await auth.user(payload.uid);
        user.password = undefined;
        user.entries = undefined;
        return user;
    },
    schema: {
        headers: {
            type: "object",
            properties: {
                authorization: { type: "string" },
            },
            required: ["authorization"],
        },
    },
});

app.post("/auth/login", {
    async handler(request, reply) {
        let user = await auth.findUserByEmail(request.body.email);

        if (user != null && user.password == request.body.password) {
            let token = await auth.createJwt(user.uid);

            return {
                token,
            };
        }

        reply.code(400);
        return {
            error: "invalid credentials",
        };
    },
    schema: {
        body: {
            type: "object",
            properties: {
                email: { type: "string" },
                password: { type: "string" },
            },
        },
        required: ["email", "password"],
    },
});

app.post("/auth/register", {
    async handler(request, reply) {
        if (await auth.findUserByEmail(request.body.email) == null) {
            let user = await auth.addUser({
                email: request.body.email,
                password: request.body.password,
                name: request.body.name,
                assets: [],
                limits: {
                    assetCount: 5,
                },
                entries: [],
            });

            return { token: await auth.createJwt(user.uid) };
        }

        reply.code(400);
        return {
            error: "invalid data",
        };
    },
    schema: {
        body: {
            type: "object",
            properties: {
                name: { type: "string" },
                email: { type: "string" },
                password: { type: "string" },
            },
            required: ["name", "email", "password"],
        },
    },
});

app.listen({ port: LISTEN_PORT });