/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
export const INSPECT_STEPS = ['MINUTE', 'HOUR', 'DAY'];
/** Server-side hard cap on `/inspect/entities?limit=`. */
export const INSPECT_ENTITY_LIMIT_MAX = 300;
// ── Date format ────────────────────────────────────────────────────
/** Format a `Date` into the date string OAP expects for the given
 *  step. Mirrors `Duration.getStartTimeBucket` / `getEndTimeBucket`'s
 *  accepted shapes:
 *
 *    DAY    → `yyyy-MM-dd`
 *    HOUR   → `yyyy-MM-dd HH`
 *    MINUTE → `yyyy-MM-dd HHmm`
 *
 *  All values are zero-padded; the date is interpreted in the OAP
 *  server's local timezone, so prefer feeding through `UTC` if your
 *  OAP is configured for UTC (the default for containerised deploys).
 */
export function formatInspectDate(d, step) {
    const y = d.getUTCFullYear();
    const m = String(d.getUTCMonth() + 1).padStart(2, '0');
    const day = String(d.getUTCDate()).padStart(2, '0');
    const date = `${y}-${m}-${day}`;
    if (step === 'DAY')
        return date;
    const h = String(d.getUTCHours()).padStart(2, '0');
    if (step === 'HOUR')
        return `${date} ${h}`;
    const min = String(d.getUTCMinutes()).padStart(2, '0');
    return `${date} ${h}${min}`;
}
/** True iff `s` parses as a valid date string for the given step. */
export function isInspectDate(s, step) {
    if (step === 'DAY')
        return /^\d{4}-\d{2}-\d{2}$/.test(s);
    if (step === 'HOUR')
        return /^\d{4}-\d{2}-\d{2} \d{2}$/.test(s);
    return /^\d{4}-\d{2}-\d{2} \d{4}$/.test(s);
}
export class InspectApiError extends Error {
    status;
    body;
    url;
    constructor(status, body, url) {
        const detail = typeof body === 'string' ? body : body.error;
        super(`${status} on ${url} — ${detail}`);
        this.status = status;
        this.body = body;
        this.url = url;
        this.name = 'InspectApiError';
    }
}
export class InspectClient {
    fetchImpl;
    base;
    defaultHeaders;
    timeoutMs;
    constructor(options) {
        this.fetchImpl = options.fetch ?? globalThis.fetch.bind(globalThis);
        this.base = options.adminUrl.replace(/\/$/, '');
        this.defaultHeaders = options.headers ?? {};
        this.timeoutMs = options.timeoutMs ?? 0;
    }
    /** `GET /inspect/metrics` with optional `regex` / `type` / `catalog`
     *  / `mqeQueryable` filters. Returns the full catalog when no
     *  filters are passed. */
    async listMetrics(args = {}) {
        const params = new URLSearchParams();
        if (args.regex !== undefined)
            params.set('regex', args.regex);
        if (args.mqeQueryable === true)
            params.set('mqeQueryable', 'true');
        for (const t of args.type ?? [])
            params.append('type', t);
        for (const c of args.catalog ?? [])
            params.append('catalog', c);
        const qs = params.toString();
        const url = `${this.base}/inspect/metrics${qs ? `?${qs}` : ''}`;
        const res = await this.send(url, { method: 'GET' });
        if (!res.ok)
            throw await this.toError(res, url);
        return (await res.json());
    }
    /** `GET /inspect/entities` — `metric` + time range + step + limit. */
    async listEntities(args) {
        const params = new URLSearchParams({
            metric: args.metric,
            start: args.start,
            end: args.end,
            step: args.step,
        });
        if (args.limit !== undefined)
            params.set('limit', String(args.limit));
        const url = `${this.base}/inspect/entities?${params.toString()}`;
        const res = await this.send(url, { method: 'GET' });
        if (!res.ok)
            throw await this.toError(res, url);
        return (await res.json());
    }
    // ── private helpers ─────────────────────────────────────────────
    async send(url, init) {
        const headers = {
            Accept: 'application/json',
            ...this.defaultHeaders,
            ...(init.headers ?? {}),
        };
        const finalInit = { ...init, headers };
        if (this.timeoutMs > 0) {
            const controller = new AbortController();
            const timer = setTimeout(() => controller.abort(), this.timeoutMs);
            try {
                return await this.fetchImpl(url, { ...finalInit, signal: controller.signal });
            }
            finally {
                clearTimeout(timer);
            }
        }
        return this.fetchImpl(url, finalInit);
    }
    async toError(res, url) {
        const text = await res.text();
        let parsed = text;
        try {
            const json = JSON.parse(text);
            if (typeof json.error === 'string') {
                parsed = json;
            }
        }
        catch {
            // not JSON; keep the raw text.
        }
        return new InspectApiError(res.status, parsed, url);
    }
}
//# sourceMappingURL=inspect.js.map