
const mime = require("mime-types");
const fs = require('fs');
const Path = require('path');
const Security = require('../src/security.js');
const MediaService = require('../model/mediaService.js');
const MediaFileMetaModel = require('../model/mediaItemMeta.js').MediaFileMetaModel;
const MediaFileTagModel = require('../model/mediaItemTag.js').MediaFileTagModel;
const { AccessModel, ACCESS_TYPE, ACCESS_GRANT, ACCESS_TO } = require('../model/access.js');

function MediaToJson(mediaData) {
    if (!mediaData)
        return null;
    if (mediaData.accessType === ACCESS_GRANT.readNoMeta)
        mediaData.meta = {
            height: mediaData.meta?.height,
            width: mediaData.meta?.width
        };
    return mediaData;
}

module.exports = { register: app => {
    app.router.post("/api/database/reload", (req, res) => {
        app.routerUtils.onApiRequest(req, res);
        if (!req.sessionObj?.accessList?.isAdmin)
            return app.routerUtils.onBadRequest(res);
        app.libraryManager.updateLibraries(app.databaseHelper).finally(x => { require('../src/autotagBuilder').rebuildPathTags(app); });
        app.routerUtils.jsonResponse(res, {});
    });
    app.router.get("/api/access/list", (req, res) => {
        app.routerUtils.onApiRequest(req, res);
        app.routerUtils.jsonResponse(res, req.sessionObj?.accessList || {});
    });
    app.router.post("/api/access/link", async (req, res) => { // /api/access/link, post: { linkIds: [string] (JSON) }
        app.routerUtils.onApiRequest(req, res);
        if (!req.post?.linkIds?.length)
            return app.routerUtils.httpResponse(res, 400, "Missing argument");
        try {
            for (let i of JSON.parse(req.post.linkIds)) {
                const access = await app.databaseHelper.findOne(AccessModel, { type: ACCESS_TYPE.link, typeData: i });
                if (access) {
                    Security.addLinkToSession(req, access.id, i);
                    if (access.accessTo == ACCESS_TO.admin)
                        Security.setAdmin(req, true);
                }
            }
        }
        catch (err) {
            console.error(err);
            return app.routerUtils.onBadRequest(res);
        }
        app.routerUtils.jsonResponse(res, req.sessionObj.accessList);
    });
    app.router.del("/api/access/:id", async (req, res) => {
        app.routerUtils.onApiRequest(req, res);
        Security.removeFromSession(req, req.params.id);
        const access = await app.databaseHelper.fetch(AccessModel, { id: Object.keys(req.sessionObj.accessList).map(i => req.sessionObj.accessList[i]).filter(x => x.dbId).map(x => x.dbId), accessTo: ACCESS_TO.admin });
        const result = Security.setAdmin(req, !!(access?.length || 0));
        app.routerUtils.jsonResponse(res, result);
    });
    app.router.post("/api/media/:id/tag/del/:tag", async (req, res) => {
        app.routerUtils.onApiRequest(req, res);
        if (!req.params.id ||!req.params.tag)
            return app.routerUtils.onBadRequest(res);
        let checksum = [ req.params.id ];
        if (req.params.id === "list") {
            if (!req.body?.['list[]'])
                return app.routerUtils.onBadRequest(res);
            checksum = req.body['list[]'];
        }

        let data = await MediaService.fetchMultiple(app, checksum, req.sessionObj?.accessList, 0);
        data = Object.keys(data).map(x => data[x]).filter(x => x.ACCESS_TYPE != ACCESS_GRANT.write);
        await Promise.all(data.map(x => MediaService.updateVersionInDb(app, x.fixedSum)));

        await app.databaseHelper.remove(MediaFileTagModel, { md5sum: data.map(x => x.fixedSum), tag: decodeURIComponent(req.params.tag), fromMeta: 0 });
        const allMedias = await MediaService.fetchMultiple(app, checksum, req.sessionObj?.accessList, 0);
        app.routerUtils.jsonResponse(res, Object.keys(allMedias).map(x => allMedias[x]).map(x => MediaToJson(x)));
    });
    app.router.put("/api/media/:id/tag", async (req, res) => {
        app.routerUtils.onApiRequest(req, res);
        const requestedTag = req.body?.tag;
        if (!req.params.id ||!requestedTag)
            return app.routerUtils.onBadRequest(res);

        let checksum = [ req.params.id ];
        if (req.params.id === "list") {
            if (!req.body?.['list[]'])
                return app.routerUtils.onBadRequest(res);
            checksum = req.body['list[]'];
        }

        let data = await MediaService.fetchMultiple(app, checksum, req.sessionObj?.accessList, 0);
        data = Object.keys(data)
            .map(x => data[x])
            .filter(x => {
                if (x.ACCESS_TYPE != ACCESS_GRANT.write)
                    return true;
                for (let existingTag of [...x.tags, ...x.fixedTags]) {
                    if (existingTag === requestedTag || existingTag.startsWith(`${requestedTag}/`)) {
                        return true;
                    }
                }
            });
        await Promise.all(data.map(x => MediaService.updateVersionInDb(app, x.fixedSum)));

        let tag = data.map(x => new MediaFileTagModel(x.fixedSum, requestedTag, false));
        await app.databaseHelper.insertMultipleSameTable(tag);
        const allMedias = await MediaService.fetchMultiple(app, checksum, req.sessionObj?.accessList, 0);
        app.routerUtils.jsonResponse(res, Object.keys(allMedias).map(x => allMedias[x]).map(x => MediaToJson(x)));
    });
    app.router.patch("/api/media/:id/meta/:key", async (req, res) => {
        app.routerUtils.onApiRequest(req, res);
        if (!req.params.id ||!req.params.key || !Number.isInteger(req.body?.value?.length))
            return app.routerUtils.onBadRequest(res);

        let checksum = [ req.params.id ];
        if (req.params.id === "list") {
            if (!req.body?.['list[]'])
                return app.routerUtils.onBadRequest(res);
            checksum = req.body['list[]'];
        }

        let data = await MediaService.fetchMultiple(app, checksum, req.sessionObj?.accessList, 0);
        data = Object.keys(data)
            .map(x => data[x])
            .filter(x => x.ACCESS_TYPE != ACCESS_GRANT.write);
        await Promise.all(data.map(x => MediaService.updateVersionInDb(app, x.fixedSum)));

        if (!req.body.value) {
            await app.databaseHelper.remove(MediaFileMetaModel, { md5sum: data.map(x => x.fixedSum), key: req.params.key, fromFile: 0 });
        } else {
            let newMediaItemMedia = data.map(x => new MediaFileMetaModel(x.fixedSum, req.params.key, req.body.value, false));
            await app.databaseHelper.remove(MediaFileMetaModel, { md5sum: data.map(x => x.fixedSum), key: req.params.key, fromFile: 0 });
            await app.databaseHelper.insertMultipleSameTable(newMediaItemMedia);
        }
        const allMedias = await MediaService.fetchMultiple(app, checksum, req.sessionObj?.accessList);
        app.routerUtils.jsonResponse(res, Object.keys(allMedias).map(x => allMedias[x]).map(x => MediaToJson(x)));
    });
    app.router.get("/api/media/list", async (req, res) => {
        app.routerUtils.onApiRequest(req, res);
        let first = undefined,
            last = undefined,
            maxVersion = undefined;
        if (req.body?.chronology !== undefined) {
            let range = await MediaService.getMediaRange(app);
            first = range.min;
            last = range.max;
            maxVersion = range.maxVersion;
        }
        let fromDate = parseInt(req.body?.from);
        let count = parseInt(req.body?.count);
        app.routerUtils.jsonResponse(res, {
            data: (await MediaService.fetchMediasWithAccess(
                app,
                isNaN(fromDate) ? 0 : fromDate,
                isNaN(count) ? 25 : Math.min(350, count),
                req.sessionObj?.accessList,
                req.body?.version || 0)).map(MediaToJson),
            first: first,
            last: last,
            maxVersion: maxVersion
        });
    });
    app.router.get("/api/media/:md5sum", async (req, res) => {
        app.routerUtils.onApiRequest(req, res);
        let data = MediaToJson(await MediaService.fetchOne(app, req.params.md5sum, req.sessionObj?.accessList, 0));
        if (!data)
            return app.routerUtils.onPageNotFound(res);
        app.routerUtils.jsonResponse(res, data);
    });
    app.router.get("/api/media/thumbnail/:md5sum.jpg", async (req, res) => {
        app.routerUtils.onApiRequest(req, res);
        let data = await MediaService.fetchOne(app, req.params.md5sum, req.sessionObj?.accessList, 0);
        if (!data)
            return app.routerUtils.onPageNotFound(res);
        try {
            let thumbnail = null;
            req.body = req.body || {};
            req.body.w = parseInt(req.body.w || 0);
            req.body.h = parseInt(req.body.h || 0);
            req.body.q = parseInt(req.body.q || 6);
            try {
                thumbnail = await (await app.libraryManager.findMedia(data.path))?.createThumbnail(req.body.w, req.body.h, req.body.q);
            } catch (err) {
                return app.routerUtils.apiError(res);
            }
            if (!thumbnail)
                return app.routerUtils.onPageNotFound(res);
            res.setHeader("Content-Type", "image/jpeg");
            res.setHeader("Content-Length", fs.statSync(thumbnail.name)?.size || undefined);
            res.setHeader("Cache-Control", "private, max-age=2630000"); // 1 month cache
            let rd = fs.createReadStream(thumbnail.name);
            rd.once('end', () => thumbnail.removeCallback());
            rd.pipe(res);
        }
        catch (err) {
            console.error(err);
            app.routerUtils.onPageNotFound(res);
        }
    });
    app.router.get("/api/media/original/:md5sum", async (req, res) => {
        app.routerUtils.onApiRequest(req, res);
        let data = await MediaService.fetchOne(app, req.params.md5sum, req.sessionObj?.accessList, 0);
        if (!data)
            return app.routerUtils.onPageNotFound(res);
        const fileName = Path.basename(data.path);
        res.setHeader("Cache-Control", "private, max-age=2630000"); // 1 month cache
        if (data.accessType === ACCESS_GRANT.readNoMeta || req.body?.trim !== undefined) {
            console.log("remove meta");//-> trim metadata
        }
        res.setHeader("Content-Disposition", `attachment; filename="${fileName}"`);
        res.setHeader("Content-Type", mime.lookup(data.path));
        res.setHeader("Content-Length", fs.statSync(data.path)?.size || undefined);
        fs.createReadStream(data.path).pipe(res);
    });
}};

