"use strict";
/*
 * server component for the TimeLimit App
 * Copyright (C) 2019 - 2026 Jonas Lochmann
 *
 * 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, version 3 of the License.
 *
 * 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/>.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.createAdminRouter = void 0;
const body_parser_1 = require("body-parser");
const express_1 = require("express");
const http_errors_1 = require("http-errors");
const Sequelize = require("sequelize");
const purchase_1 = require("../function/purchase");
const statusmessage_1 = require("../function/statusmessage");
const token_1 = require("../util/token");
const identity_token_1 = require("../util/identity-token");
const createAdminRouter = ({ database, websocket, eventHandler }) => {
    const router = (0, express_1.Router)();
    router.get('/status', async (_, res, next) => {
        try {
            const { counters, maxValues } = await eventHandler.getValues();
            res.json({
                websocketClients: websocket.countConnections(),
                counters,
                maxValues
            });
        }
        catch (ex) {
            next(ex);
        }
    });
    router.post('/reset-counters', async (_, res, next) => {
        try {
            await eventHandler.reset();
            res.json({ ok: true });
        }
        catch (ex) {
            next(ex);
        }
    });
    router.get('/status-message', async (_, res, next) => {
        try {
            const currentStatusMessage = await (0, statusmessage_1.getStatusMessage)({ database });
            res.json({
                statusMessage: currentStatusMessage
            });
        }
        catch (ex) {
            next(ex);
        }
    });
    router.post('/status-message', (0, body_parser_1.json)(), async (req, res, next) => {
        try {
            if (typeof req.body !== 'object' || typeof req.body.message !== 'string') {
                throw new http_errors_1.BadRequest();
            }
            const newStatusMessage = req.body.message;
            await (0, statusmessage_1.setStatusMessage)({ database, newStatusMessage });
            websocket.triggerImportantSyncAtAllDevicesInBackground();
            res.json({ ok: true });
        }
        catch (ex) {
            next(ex);
        }
    });
    router.post('/unlock-premium', (0, body_parser_1.json)(), async (req, res, next) => {
        try {
            if (typeof req.body !== 'object' || typeof req.body.mail !== 'string' || typeof req.body.duration !== 'string') {
                throw new http_errors_1.BadRequest();
            }
            const mail = req.body.mail;
            const type = req.body.duration;
            if (type !== 'month' && type !== 'year' || mail === '') {
                throw new http_errors_1.BadRequest();
            }
            await database.transaction(async (transaction) => {
                const userEntryUnsafe = await database.user.findOne({
                    where: {
                        mail
                    },
                    attributes: ['familyId'],
                    transaction
                });
                if (!userEntryUnsafe) {
                    throw new http_errors_1.Conflict('no user with specified mail address');
                }
                const userEntry = {
                    familyId: userEntryUnsafe.familyId
                };
                await (0, purchase_1.addPurchase)({
                    database,
                    familyId: userEntry.familyId,
                    type,
                    service: 'directpurchase',
                    transactionId: 'legacyunlock-' + type + '-' + (0, token_1.generatePurchaseId)(),
                    websocket,
                    transaction
                });
            });
            res.json({ ok: true });
        }
        catch (ex) {
            next(ex);
        }
    });
    router.post('/unlock-premium-v2', (0, body_parser_1.json)(), async (req, res, next) => {
        try {
            if (typeof req.body !== 'object' ||
                typeof req.body.purchaseToken !== 'string' ||
                typeof req.body.purchaseId !== 'string' ||
                typeof req.body.dryRun !== 'boolean' ||
                typeof req.body.type !== 'string') {
                throw new http_errors_1.BadRequest();
            }
            const purchaseToken = req.body.purchaseToken;
            const purchaseId = req.body.purchaseId;
            const dryRun = req.body.dryRun;
            const type = req.body.type;
            if (type !== 'month' && type !== 'year' && type !== 'unpaid14') {
                throw new http_errors_1.BadRequest();
            }
            const tokenContent = await (0, identity_token_1.verifyIdentitifyToken)(purchaseToken, dryRun);
            if (tokenContent.purpose !== 'purchase') {
                res.json({ ok: false, error: 'token invalid', detail: 'wrong purpose' });
                return;
            }
            const response = await database.transaction(async (transaction) => {
                const userValid = await database.user.count({
                    where: {
                        familyId: tokenContent.familyId,
                        userId: tokenContent.userId,
                        mail: tokenContent.mail,
                        type: 'parent'
                    },
                    transaction
                });
                if (!userValid)
                    return {
                        ok: false,
                        error: 'token invalid',
                        detail: 'user not found'
                    };
                let mailToReturn;
                if (tokenContent.mail !== '')
                    mailToReturn = tokenContent.mail;
                else {
                    const userEntryWithMail = await database.user.findOne({
                        where: {
                            familyId: tokenContent.familyId,
                            mail: {
                                [Sequelize.Op.ne]: ''
                            },
                            type: 'parent'
                        },
                        transaction
                    });
                    if (!userEntryWithMail)
                        return {
                            ok: false,
                            error: 'illegal state',
                            detail: 'no user with mail found'
                        };
                    mailToReturn = userEntryWithMail.mail;
                }
                let wasAlreadyExecuted;
                const oldPurchaseByPurchaseId = await database.purchase.findOne({
                    where: {
                        service: 'directpurchase',
                        transactionId: purchaseId
                    }
                });
                if (oldPurchaseByPurchaseId === null)
                    wasAlreadyExecuted = false;
                else if (oldPurchaseByPurchaseId.familyId === tokenContent.familyId)
                    wasAlreadyExecuted = true;
                else
                    return {
                        ok: false,
                        error: 'purchase id already used'
                    };
                if (!wasAlreadyExecuted) {
                    const familyEntry = await database.family.findOne({
                        where: {
                            familyId: tokenContent.familyId
                        },
                        transaction
                    });
                    if (!familyEntry)
                        return {
                            ok: false,
                            error: 'family not found'
                        };
                    const canDoPurchase = (0, purchase_1.canDoNextPurchase)({
                        fullVersionUntil: parseInt(familyEntry.fullVersionUntil),
                        fullVersionDebts: parseInt(familyEntry.fullVersionDebts),
                    });
                    if (!canDoPurchase) {
                        const lastPurchase = await database.purchase.findOne({
                            where: {
                                familyId: tokenContent.familyId
                            },
                            transaction,
                            order: [['loggedAt', 'DESC']],
                            limit: 1
                        });
                        return {
                            ok: false,
                            error: 'can not renew now',
                            lastPurchase: lastPurchase ? {
                                service: lastPurchase.service,
                                transactionId: lastPurchase.transactionId,
                                timestamp: parseInt(lastPurchase.loggedAt),
                                timestring: new Date(parseInt(lastPurchase.loggedAt)).toISOString()
                            } : undefined
                        };
                    }
                    if (!dryRun) {
                        await (0, purchase_1.addPurchase)({
                            database,
                            familyId: tokenContent.familyId,
                            type,
                            service: 'directpurchase',
                            transactionId: purchaseId,
                            websocket,
                            transaction
                        });
                    }
                }
                return {
                    ok: true,
                    mail: mailToReturn,
                    wasAlreadyExecuted
                };
            });
            res.json(response);
        }
        catch (ex) {
            if (ex instanceof identity_token_1.TokenValidationException)
                res.json({
                    ok: false,
                    error: 'token invalid',
                    detail: ex.message
                });
            else
                next(ex);
        }
    });
    return router;
};
exports.createAdminRouter = createAdminRouter;
//# sourceMappingURL=admin.js.map