import { AUTOMATION_PATHS, AUTOMATION_USER_AGENTS } from "./policy.js";
export const DEFAULT_LOKI_FIELDS = {
    host: process.env.LOKI_FIELD_HOST ?? "RequestHost",
    path: process.env.LOKI_FIELD_PATH ?? "RequestPath",
    userAgent: process.env.LOKI_FIELD_UA ?? "request_User_Agent",
};
function escapeRe(s) {
    return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
/** Regex (anchored) matching automation request paths from the shared denylist. */
export function automationPathRegex() {
    const alts = AUTOMATION_PATHS.map((p) => p.endsWith("/") ? `${escapeRe(p)}.*` : escapeRe(p));
    return `^(${alts.join("|")})$`;
}
/** Case-insensitive regex matching automation user-agents from the shared denylist. */
export function automationUaRegex() {
    return `(?i)(${AUTOMATION_USER_AGENTS.map(escapeRe).join("|")})`;
}
/** Stream selector for a single host's Traefik access logs. */
function hostStream(selector, host, fields) {
    return `${selector} | json | ${fields.host}=\`${host}\``;
}
/**
 * LogQL instant query counting *proper* requests to `host` over the trailing
 * window — requests whose path and user-agent are NOT on the automation denylist.
 */
export function buildProperRequestCountQuery(selector, host, windowSeconds, fields = DEFAULT_LOKI_FIELDS) {
    const stream = `${hostStream(selector, host, fields)}` +
        ` | ${fields.path}!~\`${automationPathRegex()}\`` +
        ` | ${fields.userAgent}!~\`${automationUaRegex()}\``;
    return `sum(count_over_time((${stream})[${windowSeconds}s]))`;
}
/** LogQL counting ALL requests to `host` (incl. automation) over the window. */
export function buildTotalRequestCountQuery(selector, host, windowSeconds, fields = DEFAULT_LOKI_FIELDS) {
    return `sum(count_over_time((${hostStream(selector, host, fields)})[${windowSeconds}s]))`;
}
export function createLokiClient(opts) {
    const fields = opts.fields ?? DEFAULT_LOKI_FIELDS;
    const timeout = opts.fetchTimeoutMs ?? 10_000;
    async function count(query) {
        const url = `${opts.lokiUrl}/loki/api/v1/query?query=${encodeURIComponent(query)}`;
        const ac = new AbortController();
        const t = setTimeout(() => ac.abort(), timeout);
        try {
            const res = await fetch(url, { signal: ac.signal });
            if (!res.ok)
                throw new Error(`loki ${res.status}`);
            const body = (await res.json());
            return (body.data?.result ?? []).reduce((acc, r) => acc + Number(r.value?.[1] ?? 0), 0);
        }
        finally {
            clearTimeout(t);
        }
    }
    return {
        async classifyHostActivity(host, windowSeconds) {
            try {
                const proper = await count(buildProperRequestCountQuery(opts.selector, host, windowSeconds, fields));
                if (proper > 0)
                    return "ACTIVE";
                // No proper requests — but is that real idle, or just no logs at all?
                const total = await count(buildTotalRequestCountQuery(opts.selector, host, windowSeconds, fields));
                if (total === 0)
                    return "UNKNOWN"; // no access logs ⇒ can't measure
                return "IDLE"; // logs exist, all automation
            }
            catch (err) {
                // Fail-safe: never sleep something we can't measure.
                opts.logger?.warn(`[loki] query failed for ${host}: ${err instanceof Error ? err.message : String(err)} — UNKNOWN (treated as active)`);
                return "UNKNOWN";
            }
        },
    };
}
