"use strict";
// SPDX-License-Identifier: AGPL-3.0-or-later
// Copyright (C) 2024-2026 Gracker (Chris)
// This file is part of SmartPerfetto. See LICENSE for details.
Object.defineProperty(exports, "__esModule", { value: true });
exports.resetTenantPurgeJobsForTests = resetTenantPurgeJobsForTests;
const express_1 = require("express");
const auth_1 = require("../middleware/auth");
const enterpriseDb_1 = require("../services/enterpriseDb");
const enterpriseAdminControlPlaneService_1 = require("../services/enterpriseAdminControlPlaneService");
const enterpriseTenantLifecycleService_1 = require("../services/enterpriseTenantLifecycleService");
const rbac_1 = require("../services/rbac");
const router = (0, express_1.Router)();
const tenantPurgeJobs = new Map();
function canDeleteTenant(context) {
    return context.scopes.includes('*')
        || context.scopes.includes('tenant:delete')
        || context.roles.includes('org_admin');
}
function canManageTenant(context) {
    return context.scopes.includes('*')
        || context.scopes.includes('tenant:manage')
        || context.roles.includes('org_admin');
}
function canManageWorkspace(context, workspaceId) {
    return canManageTenant(context)
        || context.scopes.includes('workspace:manage')
        || context.scopes.includes('quota:manage')
        || (context.roles.includes('workspace_admin') && context.workspaceId === workspaceId);
}
function requireTenantDeletePermission(context, res) {
    if (canDeleteTenant(context))
        return true;
    (0, rbac_1.sendForbidden)(res, 'Tenant deletion requires org_admin or tenant:delete scope');
    return false;
}
function requireTenantManagePermission(context, res) {
    if (canManageTenant(context))
        return true;
    (0, rbac_1.sendForbidden)(res, 'Tenant management requires org_admin or tenant:manage scope');
    return false;
}
function requireWorkspaceManagePermission(context, workspaceId, res) {
    if (canManageWorkspace(context, workspaceId))
        return true;
    (0, rbac_1.sendForbidden)(res, 'Workspace management requires workspace_admin, workspace:manage, quota:manage, or tenant:manage');
    return false;
}
function sendControlPlaneError(res, error) {
    if (error instanceof enterpriseAdminControlPlaneService_1.EnterpriseAdminControlPlaneError) {
        res.status(error.status).json({ success: false, error: error.message });
        return;
    }
    const message = error instanceof Error ? error.message : String(error);
    res.status(500).json({ success: false, error: message || 'Enterprise admin control plane failed' });
}
function requireTenantConfirmation(body, tenantId) {
    const input = body && typeof body === 'object' ? body : {};
    return input.confirmTenantId === tenantId ? null : 'confirmTenantId must match the request tenant';
}
router.use(auth_1.authenticate);
router.get('/admin/summary', (req, res) => {
    const context = (0, auth_1.requireRequestContext)(req);
    if (!requireTenantManagePermission(context, res))
        return;
    const db = (0, enterpriseDb_1.openEnterpriseDb)();
    try {
        res.json((0, enterpriseAdminControlPlaneService_1.getEnterpriseAdminControlPlaneSummary)(db, context));
    }
    catch (error) {
        sendControlPlaneError(res, error);
    }
    finally {
        db.close();
    }
});
router.get('/workspaces', (req, res) => {
    const context = (0, auth_1.requireRequestContext)(req);
    if (!requireTenantManagePermission(context, res))
        return;
    const db = (0, enterpriseDb_1.openEnterpriseDb)();
    try {
        res.json((0, enterpriseAdminControlPlaneService_1.listEnterpriseWorkspaces)(db, context));
    }
    catch (error) {
        sendControlPlaneError(res, error);
    }
    finally {
        db.close();
    }
});
router.post('/workspaces', (req, res) => {
    const context = (0, auth_1.requireRequestContext)(req);
    if (!requireTenantManagePermission(context, res))
        return;
    const db = (0, enterpriseDb_1.openEnterpriseDb)();
    try {
        res.status(201).json((0, enterpriseAdminControlPlaneService_1.createEnterpriseWorkspace)(db, context, req.body));
    }
    catch (error) {
        sendControlPlaneError(res, error);
    }
    finally {
        db.close();
    }
});
router.patch('/workspaces/:workspaceId', (req, res) => {
    const context = (0, auth_1.requireRequestContext)(req);
    if (!requireWorkspaceManagePermission(context, req.params.workspaceId, res))
        return;
    const db = (0, enterpriseDb_1.openEnterpriseDb)();
    try {
        res.json((0, enterpriseAdminControlPlaneService_1.updateEnterpriseWorkspace)(db, context, req.params.workspaceId, req.body));
    }
    catch (error) {
        sendControlPlaneError(res, error);
    }
    finally {
        db.close();
    }
});
router.patch('/workspaces/:workspaceId/policies', (req, res) => {
    const context = (0, auth_1.requireRequestContext)(req);
    if (!requireWorkspaceManagePermission(context, req.params.workspaceId, res))
        return;
    const db = (0, enterpriseDb_1.openEnterpriseDb)();
    try {
        res.json((0, enterpriseAdminControlPlaneService_1.updateEnterpriseWorkspace)(db, context, req.params.workspaceId, req.body, Date.now(), 'tenant.workspace.policy_updated'));
    }
    catch (error) {
        sendControlPlaneError(res, error);
    }
    finally {
        db.close();
    }
});
router.get('/workspaces/:workspaceId/members', (req, res) => {
    const context = (0, auth_1.requireRequestContext)(req);
    if (!requireWorkspaceManagePermission(context, req.params.workspaceId, res))
        return;
    const db = (0, enterpriseDb_1.openEnterpriseDb)();
    try {
        res.json((0, enterpriseAdminControlPlaneService_1.listEnterpriseWorkspaceMembers)(db, context, req.params.workspaceId));
    }
    catch (error) {
        sendControlPlaneError(res, error);
    }
    finally {
        db.close();
    }
});
router.put('/workspaces/:workspaceId/members/:userId', (req, res) => {
    const context = (0, auth_1.requireRequestContext)(req);
    if (!requireWorkspaceManagePermission(context, req.params.workspaceId, res))
        return;
    const db = (0, enterpriseDb_1.openEnterpriseDb)();
    try {
        res.json((0, enterpriseAdminControlPlaneService_1.upsertEnterpriseWorkspaceMember)(db, context, req.params.workspaceId, req.params.userId, req.body ?? {}));
    }
    catch (error) {
        sendControlPlaneError(res, error);
    }
    finally {
        db.close();
    }
});
router.delete('/workspaces/:workspaceId/members/:userId', (req, res) => {
    const context = (0, auth_1.requireRequestContext)(req);
    if (!requireWorkspaceManagePermission(context, req.params.workspaceId, res))
        return;
    const db = (0, enterpriseDb_1.openEnterpriseDb)();
    try {
        res.json((0, enterpriseAdminControlPlaneService_1.deleteEnterpriseWorkspaceMember)(db, context, req.params.workspaceId, req.params.userId));
    }
    catch (error) {
        sendControlPlaneError(res, error);
    }
    finally {
        db.close();
    }
});
router.post('/tombstone', (req, res) => {
    const context = (0, auth_1.requireRequestContext)(req);
    if (!requireTenantDeletePermission(context, res))
        return;
    const confirmationError = requireTenantConfirmation(req.body, context.tenantId);
    if (confirmationError) {
        return res.status(400).json({ success: false, error: confirmationError });
    }
    const db = (0, enterpriseDb_1.openEnterpriseDb)();
    try {
        const reason = typeof req.body?.reason === 'string' ? req.body.reason : undefined;
        const tombstone = (0, enterpriseTenantLifecycleService_1.createTenantTombstone)(db, context, reason);
        res.status(202).json({
            success: true,
            tombstone,
        });
    }
    catch (error) {
        res.status(500).json({
            success: false,
            error: error.message || 'Failed to tombstone tenant',
        });
    }
    finally {
        db.close();
    }
});
router.get('/tombstone', (req, res) => {
    const context = (0, auth_1.requireRequestContext)(req);
    if (!requireTenantDeletePermission(context, res))
        return;
    const db = (0, enterpriseDb_1.openEnterpriseDb)();
    try {
        res.json({
            success: true,
            tombstone: (0, enterpriseTenantLifecycleService_1.getTenantTombstone)(db, context.tenantId),
        });
    }
    finally {
        db.close();
    }
});
router.post('/purge', (req, res) => {
    const context = (0, auth_1.requireRequestContext)(req);
    if (!requireTenantDeletePermission(context, res))
        return;
    const confirmationError = requireTenantConfirmation(req.body, context.tenantId);
    if (confirmationError) {
        return res.status(400).json({ success: false, error: confirmationError });
    }
    const db = (0, enterpriseDb_1.openEnterpriseDb)();
    try {
        const tombstone = (0, enterpriseTenantLifecycleService_1.getTenantTombstone)(db, context.tenantId);
        if (!tombstone) {
            return res.status(404).json({
                success: false,
                error: 'Tenant tombstone not found',
            });
        }
        if (tombstone.purgeAfter > Date.now()) {
            return res.status(409).json({
                success: false,
                code: 'TENANT_PURGE_WINDOW_ACTIVE',
                error: 'Tenant purge window has not elapsed',
                purgeAfter: tombstone.purgeAfter,
            });
        }
    }
    finally {
        db.close();
    }
    const jobId = `tenant-purge-${context.tenantId}-${Date.now()}`;
    const job = {
        id: jobId,
        tenantId: context.tenantId,
        status: 'running',
        createdAt: Date.now(),
    };
    tenantPurgeJobs.set(jobId, job);
    setImmediate(async () => {
        const jobDb = (0, enterpriseDb_1.openEnterpriseDb)();
        try {
            job.proof = await (0, enterpriseTenantLifecycleService_1.purgeTenantNow)(jobDb, context);
            job.status = 'completed';
            job.completedAt = Date.now();
        }
        catch (error) {
            job.completedAt = Date.now();
            if (error instanceof enterpriseTenantLifecycleService_1.TenantPurgeBlockedError) {
                job.status = 'blocked';
                job.blockers = error.blockers;
            }
            else if (error instanceof enterpriseTenantLifecycleService_1.TenantPurgeWindowError) {
                job.status = 'failed';
                job.error = error.message;
            }
            else {
                job.status = 'failed';
                job.error = error.message || 'Tenant purge failed';
            }
        }
        finally {
            jobDb.close();
        }
    });
    res.status(202).json({
        success: true,
        jobId,
        status: job.status,
    });
});
router.get('/purge/:jobId', (req, res) => {
    const context = (0, auth_1.requireRequestContext)(req);
    if (!requireTenantDeletePermission(context, res))
        return;
    const job = tenantPurgeJobs.get(req.params.jobId);
    if (!job || job.tenantId !== context.tenantId) {
        return res.status(404).json({
            success: false,
            error: 'Tenant purge job not found',
        });
    }
    res.json({
        success: true,
        job,
    });
});
function resetTenantPurgeJobsForTests() {
    tenantPurgeJobs.clear();
}
exports.default = router;
//# sourceMappingURL=enterpriseTenantRoutes.js.map