import KubernetesResource from "../../Models/DatabaseModels/KubernetesResource";
import BadDataException from "../../Types/Exception/BadDataException";
import ObjectID from "../../Types/ObjectID";
import KubernetesResourceService from "../Services/KubernetesResourceService";
import KubernetesClusterService from "../Services/KubernetesClusterService";
import UserMiddleware from "../Middleware/UserAuthorization";
import Response from "../Utils/Response";
import BaseAPI from "./BaseAPI";
import CommonAPI from "./CommonAPI";
import NotFoundException from "../../Types/Exception/NotFoundException";
/*
 * ------------------------------------------------------------------
 * KubernetesResourceAPI
 *
 * Augments the auto-generated CRUD router with a single custom
 * endpoint the Kubernetes overview page uses to fetch counts and
 * health summaries in one round-trip:
 *
 *   POST /kubernetes-resource/inventory-summary/:clusterId
 *
 * The standard CRUD endpoints (list / get) are still registered by
 * BaseAPI; the UI uses them via ModelAPI for detail-page reads.
 * Write endpoints reject (@TableAccessControl create/update/delete
 * = []); ingest writes go through KubernetesResourceService as root.
 * ------------------------------------------------------------------
 */
export default class KubernetesResourceAPI extends BaseAPI {
    constructor() {
        var _a, _b, _c;
        super(KubernetesResource, KubernetesResourceService);
        this.router.post(`${(_a = new this.entityType()
            .getCrudApiPath()) === null || _a === void 0 ? void 0 : _a.toString()}/inventory-summary/:clusterId`, UserMiddleware.getUserMiddleware, async (req, res, next) => {
            try {
                await this.getInventorySummary(req, res);
            }
            catch (err) {
                next(err);
            }
        });
        /*
         * Latest CPU+memory aggregated by Pod namespace. Powers the
         * Namespaces list view without a ClickHouse round-trip.
         */
        this.router.post(`${(_b = new this.entityType()
            .getCrudApiPath()) === null || _b === void 0 ? void 0 : _b.toString()}/latest-pod-metrics-by-namespace/:clusterId`, UserMiddleware.getUserMiddleware, async (req, res, next) => {
            try {
                await this.getLatestPodMetricsByNamespace(req, res);
            }
            catch (err) {
                next(err);
            }
        });
        /*
         * Latest CPU+memory aggregated by Pod ownerReferences[].name for
         * a given owner kind. Powers Deployments/StatefulSets/DaemonSets/
         * Jobs/CronJobs list views.
         */
        this.router.post(`${(_c = new this.entityType()
            .getCrudApiPath()) === null || _c === void 0 ? void 0 : _c.toString()}/latest-pod-metrics-by-owner/:clusterId/:ownerKind`, UserMiddleware.getUserMiddleware, async (req, res, next) => {
            try {
                await this.getLatestPodMetricsByOwner(req, res);
            }
            catch (err) {
                next(err);
            }
        });
    }
    /*
     * Cluster + auth resolution shared by the cluster-scoped sub-routes.
     * Returns the (projectId, kubernetesClusterId) tuple after enforcing
     * the standard ACL chain. Throws NotFound when the cluster is
     * missing or the caller lacks read access (indistinguishable on
     * purpose).
     */
    async resolveClusterForRequest(req) {
        const clusterIdParam = req.params["clusterId"];
        if (!clusterIdParam) {
            throw new BadDataException("Cluster ID is required");
        }
        let kubernetesClusterId;
        try {
            kubernetesClusterId = new ObjectID(clusterIdParam);
        }
        catch (_a) {
            throw new BadDataException("Invalid Cluster ID");
        }
        const props = await CommonAPI.getDatabaseCommonInteractionProps(req);
        const cluster = await KubernetesClusterService.findOneById({
            id: kubernetesClusterId,
            select: {
                _id: true,
                projectId: true,
            },
            props,
        });
        if (!cluster || !cluster.projectId) {
            throw new NotFoundException("Kubernetes Cluster not found");
        }
        return {
            projectId: cluster.projectId,
            kubernetesClusterId,
        };
    }
    /*
     * Translate a service-layer Map of aggregates into a JSON dict
     * { name: { cpuPercent, memoryBytes, memoryPercent } } suitable for
     * the wire. memoryBytes is stringified so values past 2 GiB don't
     * overflow client-side number parsing in the JSON path; the UI parses
     * it back to a number for rendering. memoryPercent is the summed
     * per-pod "% of node allocatable memory" (parallel to cpuPercent).
     */
    mapAggregatesToJson(aggregates) {
        const out = {};
        for (const [name, value] of aggregates.entries()) {
            out[name] = {
                cpuPercent: value.cpuPercent,
                memoryBytes: value.memoryBytes.toString(),
                memoryPercent: value.memoryPercent,
            };
        }
        return out;
    }
    async getLatestPodMetricsByNamespace(req, res) {
        const { projectId, kubernetesClusterId } = await this.resolveClusterForRequest(req);
        const staleAfter = new Date(Date.now() - 15 * 60 * 1000);
        const aggregates = await this.service.getLatestMetricsByNamespace({
            projectId,
            kubernetesClusterId,
            staleAfter,
        });
        return Response.sendJsonObjectResponse(req, res, {
            aggregates: this.mapAggregatesToJson(aggregates),
        });
    }
    async getLatestPodMetricsByOwner(req, res) {
        const ownerKind = req.params["ownerKind"];
        if (!ownerKind) {
            throw new BadDataException("Owner kind is required");
        }
        /*
         * Only a small allow-list of owner kinds makes sense here; reject
         * anything else so the endpoint can't be used to probe arbitrary
         * jsonb_array_elements paths.
         */
        const allowed = new Set([
            "Deployment",
            "StatefulSet",
            "DaemonSet",
            "Job",
            "CronJob",
            "ReplicaSet",
        ]);
        if (!allowed.has(ownerKind)) {
            throw new BadDataException(`Unsupported owner kind: ${ownerKind}`);
        }
        const { projectId, kubernetesClusterId } = await this.resolveClusterForRequest(req);
        const staleAfter = new Date(Date.now() - 15 * 60 * 1000);
        const aggregates = await this.service.getLatestMetricsByOwner({
            projectId,
            kubernetesClusterId,
            ownerKind,
            staleAfter,
        });
        return Response.sendJsonObjectResponse(req, res, {
            aggregates: this.mapAggregatesToJson(aggregates),
        });
    }
    async getInventorySummary(req, res) {
        const { projectId, kubernetesClusterId } = await this.resolveClusterForRequest(req);
        const summary = await this.service.getInventorySummary({
            projectId,
            kubernetesClusterId,
        });
        const responseBody = {
            countsByKind: summary.countsByKind,
            podPhaseCounts: summary.podPhaseCounts,
            nodeReadyCounts: summary.nodeReadyCounts,
            nodePressureCounts: summary.nodePressureCounts,
            // Convenience fields so the UI doesn't have to repeat COALESCE:
            pvcCount: summary.countsByKind["PersistentVolumeClaim"] || 0,
            pvCount: summary.countsByKind["PersistentVolume"] || 0,
            nodeCount: summary.countsByKind["Node"] || 0,
            podCount: summary.countsByKind["Pod"] || 0,
            namespaceCount: summary.countsByKind["Namespace"] || 0,
            deploymentCount: summary.countsByKind["Deployment"] || 0,
            statefulSetCount: summary.countsByKind["StatefulSet"] || 0,
            daemonSetCount: summary.countsByKind["DaemonSet"] || 0,
            jobCount: summary.countsByKind["Job"] || 0,
            cronJobCount: summary.countsByKind["CronJob"] || 0,
            hpaCount: summary.countsByKind["HorizontalPodAutoscaler"] || 0,
            vpaCount: summary.countsByKind["VerticalPodAutoscaler"] || 0,
            containerCount: summary.containerCount,
            degradedPods: summary.degradedPods,
            degradedNodes: summary.degradedNodes,
        };
        return Response.sendJsonObjectResponse(req, res, responseBody);
    }
}
//# sourceMappingURL=KubernetesResourceAPI.js.map