import { parsePodObject, parseNodeObject, parseDeploymentObject, parseStatefulSetObject, parseDaemonSetObject, parseJobObject, parseCronJobObject, parseNamespaceObject, parsePVCObject, parsePVObject, parseHPAObject, parseVPAObject, extractObjectFromLogBody, } from "./KubernetesObjectParser";
/**
 * k8s.resource.name (plural, lowercase from the k8sobjects receiver)
 * → Kubernetes Kind (singular, PascalCase, used throughout the UI).
 *
 * Returns null for unrecognized types — caller should skip those
 * records rather than store them under a bogus kind.
 */
export function kindFromResourceType(resourceType) {
    const map = {
        pods: "Pod",
        nodes: "Node",
        namespaces: "Namespace",
        deployments: "Deployment",
        statefulsets: "StatefulSet",
        daemonsets: "DaemonSet",
        jobs: "Job",
        cronjobs: "CronJob",
        persistentvolumeclaims: "PersistentVolumeClaim",
        persistentvolumes: "PersistentVolume",
        horizontalpodautoscalers: "HorizontalPodAutoscaler",
        verticalpodautoscalers: "VerticalPodAutoscaler",
    };
    return map[resourceType.toLowerCase()] || null;
}
export const INVENTORIED_RESOURCE_TYPES = [
    "pods",
    "nodes",
    "namespaces",
    "deployments",
    "statefulsets",
    "daemonsets",
    "jobs",
    "cronjobs",
    "persistentvolumeclaims",
    "persistentvolumes",
    "horizontalpodautoscalers",
    "verticalpodautoscalers",
];
/*
 * Parse a Kubernetes memory string ("256Mi", "1Gi", "512M", "2G") to
 * bytes. Returns null when the input is empty or unparseable so
 * callers can leave memoryLimitBytes null rather than store a wrong
 * number.
 */
function parseMemoryStringToBytes(value) {
    if (!value) {
        return null;
    }
    const match = value
        .trim()
        .match(/^([0-9]*\.?[0-9]+)\s*([A-Za-z]+)?$/);
    if (!match) {
        return null;
    }
    const num = parseFloat(match[1] || "");
    if (isNaN(num)) {
        return null;
    }
    const unit = (match[2] || "").trim();
    // Binary units (Kubernetes default for limits).
    if (unit === "Ki") {
        return Math.round(num * 1024);
    }
    if (unit === "Mi") {
        return Math.round(num * 1024 * 1024);
    }
    if (unit === "Gi") {
        return Math.round(num * 1024 * 1024 * 1024);
    }
    if (unit === "Ti") {
        return Math.round(num * 1024 * 1024 * 1024 * 1024);
    }
    // Decimal units.
    if (unit === "K" || unit === "k") {
        return Math.round(num * 1000);
    }
    if (unit === "M") {
        return Math.round(num * 1000 * 1000);
    }
    if (unit === "G") {
        return Math.round(num * 1000 * 1000 * 1000);
    }
    if (unit === "T") {
        return Math.round(num * 1000 * 1000 * 1000 * 1000);
    }
    // No unit -> raw bytes.
    if (unit === "") {
        return Math.round(num);
    }
    return null;
}
/**
 * Build per-container rows from a parsed Pod object. The state +
 * reason + ready + restartCount columns are taken from
 * status.containerStatuses (matched by container name). When status
 * hasn't been observed yet (newly scheduled pod) those fields stay
 * null.
 */
export function extractContainersFromPod(data) {
    var _a, _b, _c, _d;
    const meta = data.parsedPod.metadata;
    if (!meta || !meta.name) {
        return [];
    }
    const podNamespaceKey = meta.namespace || "";
    const podName = meta.name;
    const specContainers = Array.isArray((_a = data.parsedPod.spec) === null || _a === void 0 ? void 0 : _a.containers)
        ? data.parsedPod.spec.containers
        : [];
    const statusList = Array.isArray((_b = data.parsedPod.status) === null || _b === void 0 ? void 0 : _b.containerStatuses)
        ? data.parsedPod.status.containerStatuses
        : [];
    const statusByName = new Map();
    for (const cs of statusList) {
        if (cs && typeof cs.name === "string" && cs.name) {
            statusByName.set(cs.name, cs);
        }
    }
    const rows = [];
    for (const container of specContainers) {
        const containerName = (container === null || container === void 0 ? void 0 : container.name) || "";
        if (!containerName) {
            continue;
        }
        const cs = statusByName.get(containerName);
        const memLimitRaw = (_d = (_c = container.resources) === null || _c === void 0 ? void 0 : _c.limits) === null || _d === void 0 ? void 0 : _d["memory"];
        const memoryLimitBytes = parseMemoryStringToBytes(memLimitRaw);
        rows.push({
            podNamespaceKey,
            podName,
            name: containerName,
            image: container.image || (cs ? cs.image || null : null) || null,
            state: cs && typeof cs.state === "string" ? cs.state : null,
            reason: cs && typeof cs.reason === "string" && cs.reason ? cs.reason : null,
            isReady: cs ? Boolean(cs.ready) : null,
            restartCount: cs && typeof cs.restartCount === "number" ? cs.restartCount : null,
            memoryLimitBytes,
            lastSeenAt: data.lastSeenAt,
        });
    }
    return rows;
}
function parseCreationTimestamp(value) {
    if (!value) {
        return null;
    }
    try {
        const d = new Date(value);
        if (isNaN(d.getTime())) {
            return null;
        }
        return d;
    }
    catch (_a) {
        return null;
    }
}
function deriveNodeConditionFlags(conditions) {
    if (!conditions || conditions.length === 0) {
        return {
            isReady: null,
            hasMemoryPressure: null,
            hasDiskPressure: null,
            hasPidPressure: null,
        };
    }
    let isReady = null;
    let memPressure = null;
    let diskPressure = null;
    let pidPressure = null;
    for (const c of conditions) {
        const isTrue = c.status === "True";
        if (c.type === "Ready") {
            isReady = isTrue;
        }
        else if (c.type === "MemoryPressure") {
            memPressure = isTrue;
        }
        else if (c.type === "DiskPressure") {
            diskPressure = isTrue;
        }
        else if (c.type === "PIDPressure") {
            pidPressure = isTrue;
        }
    }
    /*
     * For pressure flags we only treat a Node as having a signal when the
     * condition was actually reported. If the agent didn't include it,
     * leave as null rather than false — avoids false-negative alerting.
     */
    return {
        isReady,
        hasMemoryPressure: memPressure === null ? false : memPressure,
        hasDiskPressure: diskPressure === null ? false : diskPressure,
        hasPidPressure: pidPressure === null ? false : pidPressure,
    };
}
function parseByResourceType(resourceType, kvList) {
    switch (resourceType.toLowerCase()) {
        case "pods":
            return parsePodObject(kvList);
        case "nodes":
            return parseNodeObject(kvList);
        case "namespaces":
            return parseNamespaceObject(kvList);
        case "deployments":
            return parseDeploymentObject(kvList);
        case "statefulsets":
            return parseStatefulSetObject(kvList);
        case "daemonsets":
            return parseDaemonSetObject(kvList);
        case "jobs":
            return parseJobObject(kvList);
        case "cronjobs":
            return parseCronJobObject(kvList);
        case "persistentvolumeclaims":
            return parsePVCObject(kvList);
        case "persistentvolumes":
            return parsePVObject(kvList);
        case "horizontalpodautoscalers":
            return parseHPAObject(kvList);
        case "verticalpodautoscalers":
            return parseVPAObject(kvList);
        default:
            return null;
    }
}
/**
 * Parse a single OTLP log body string + its k8s.resource.name into a
 * ParsedKubernetesResource (and, for Pod records, child container
 * rows) ready for upsert. Returns null when the record is malformed,
 * unsupported, or missing required identity fields.
 */
export function extractInventoryResource(data) {
    const kind = kindFromResourceType(data.resourceType);
    if (!kind) {
        return null;
    }
    const kvList = extractObjectFromLogBody(data.logBody);
    if (!kvList) {
        return null;
    }
    const parsed = parseByResourceType(data.resourceType, kvList);
    if (!parsed) {
        return null;
    }
    const metadata = parsed.metadata;
    if (!metadata || !metadata.name) {
        return null;
    }
    // Pod-specific hot columns
    let phase = null;
    let containerCount = null;
    if (kind === "Pod") {
        const podStatus = parsed.status;
        phase = (podStatus === null || podStatus === void 0 ? void 0 : podStatus.phase) || null;
        const podSpec = parsed.spec;
        containerCount = Array.isArray(podSpec === null || podSpec === void 0 ? void 0 : podSpec.containers)
            ? podSpec.containers.length
            : 0;
    }
    // Node-specific hot columns
    let nodeFlags = {
        isReady: null,
        hasMemoryPressure: null,
        hasDiskPressure: null,
        hasPidPressure: null,
    };
    if (kind === "Node") {
        const nodeStatus = parsed.status;
        nodeFlags = deriveNodeConditionFlags(nodeStatus === null || nodeStatus === void 0 ? void 0 : nodeStatus.conditions);
    }
    /*
     * Collect spec/status blocks when the typed parser exposed them.
     * Some kinds (Namespace) don't carry spec/status, so these are
     * nullable.
     * Cast to a minimal structural shape so TS permits optional access
     * across the heterogeneous union.
     */
    const anyParsed = parsed;
    const resource = {
        kind,
        namespaceKey: metadata.namespace || "",
        name: metadata.name,
        uid: metadata.uid || null,
        phase,
        isReady: nodeFlags.isReady,
        hasMemoryPressure: nodeFlags.hasMemoryPressure,
        hasDiskPressure: nodeFlags.hasDiskPressure,
        hasPidPressure: nodeFlags.hasPidPressure,
        labels: metadata.labels && Object.keys(metadata.labels).length > 0
            ? metadata.labels
            : null,
        annotations: metadata.annotations && Object.keys(metadata.annotations).length > 0
            ? metadata.annotations
            : null,
        ownerReferences: metadata.ownerReferences && metadata.ownerReferences.length > 0
            ? {
                items: metadata.ownerReferences,
            }
            : null,
        spec: anyParsed.spec || null,
        containerCount,
        status: anyParsed.status || null,
        lastSeenAt: data.lastSeenAt,
        resourceCreationTimestamp: parseCreationTimestamp(metadata.creationTimestamp),
    };
    let containers = [];
    if (kind === "Pod") {
        containers = extractContainersFromPod({
            parsedPod: parsed,
            lastSeenAt: data.lastSeenAt,
        });
    }
    return { resource, containers };
}
//# sourceMappingURL=KubernetesInventoryExtractor.js.map