"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var CliEndpointResolverService_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.CliEndpointResolverService = void 0;
const common_1 = require("@nestjs/common");
const cli_ssh_service_1 = require("./cli-ssh.service");
const nip_base_domain_util_1 = require("../lib/nip-base-domain.util");
const SYSTEM_APPS = {
    fluiApi: {
        defaultSubdomain: 'api',
        defaultScheme: 'http',
        ingressNames: ['flui-api', 'flui-api-ingress'],
        ingressLabelNames: ['flui-api'],
    },
    fluiWeb: {
        defaultSubdomain: 'app',
        defaultScheme: 'http',
        ingressNames: ['flui-web', 'flui-web-ingress'],
        ingressLabelNames: ['flui-web'],
    },
    zitadel: {
        defaultSubdomain: 'auth',
        defaultScheme: 'https',
        ingressNames: ['zitadel', 'zitadel-ingress'],
        ingressLabelNames: ['zitadel'],
    },
    grafana: {
        defaultSubdomain: null,
        defaultScheme: 'http',
        ingressNames: ['grafana-ingress'],
        ingressLabelNames: ['grafana'],
    },
    prometheus: {
        defaultSubdomain: null,
        defaultScheme: 'http',
        ingressNames: ['prometheus-ingress'],
        ingressLabelNames: ['prometheus'],
    },
    loki: {
        defaultSubdomain: null,
        defaultScheme: 'http',
        ingressNames: ['loki-ingress'],
        ingressLabelNames: ['loki'],
    },
};
let CliEndpointResolverService = CliEndpointResolverService_1 = class CliEndpointResolverService {
    constructor(sshService) {
        this.sshService = sshService;
        this.logger = new common_1.Logger(CliEndpointResolverService_1.name);
    }
    async resolveEndpoints(masterIp, nipHostnameToken) {
        const snapshot = await this.fetchSnapshot(masterIp);
        const configMapData = snapshot.configmap?.data ?? {};
        const secretData = snapshot.secret?.data ?? {};
        const authMode = configMapData['AUTH_MODE'] ?? 'unknown';
        const oidcIssuer = configMapData['OIDC_ISSUER'] ?? '';
        const oidcJwksUri = configMapData['OIDC_JWKS_URI'] ?? '';
        const oidcAudience = secretData['OIDC_AUDIENCE']
            ? Buffer.from(secretData['OIDC_AUDIENCE'], 'base64').toString('utf-8')
            : '';
        // Dashboard public client_id lives inside the flui-web-config ConfigMap's
        // `config.json` data key — written by OidcBootstrapService.patchWebConfigMap.
        let oidcClientId = '';
        const webConfigJson = snapshot.webConfigMap?.data?.['config.json'];
        if (webConfigJson) {
            try {
                const parsed = JSON.parse(webConfigJson);
                if (typeof parsed.oidcClientId === 'string') {
                    oidcClientId = parsed.oidcClientId;
                }
            }
            catch (err) {
                this.logger.warn(`flui-web-config/config.json is not valid JSON: ${err.message}`);
            }
        }
        const endpoints = {};
        for (const key of Object.keys(SYSTEM_APPS)) {
            endpoints[key] = this.resolveApp(key, SYSTEM_APPS[key], snapshot.ingresses.items ?? [], snapshot.ingressRoutes.items ?? [], masterIp, nipHostnameToken);
        }
        return {
            ...endpoints,
            authMode,
            oidcIssuer,
            oidcJwksUri,
            oidcAudience,
            oidcClientId,
        };
    }
    async fetchSnapshot(masterIp) {
        const command = [
            `ING=$(kubectl get ingress -n flui-system -o json 2>/dev/null || echo '{"items":[]}')`,
            `IR=$(kubectl get ingressroute.traefik.io -n flui-system -o json 2>/dev/null || echo '{"items":[]}')`,
            `CM=$(kubectl get configmap flui-api-config -n flui-system -o json 2>/dev/null || echo '{}')`,
            `SEC=$(kubectl get secret flui-secrets -n flui-system -o json 2>/dev/null || echo '{}')`,
            `WCM=$(kubectl get configmap flui-web-config -n flui-system -o json 2>/dev/null || echo '{}')`,
            `printf '{"ingresses":%s,"ingressRoutes":%s,"configmap":%s,"secret":%s,"webConfigMap":%s}' "$ING" "$IR" "$CM" "$SEC" "$WCM"`,
        ].join('; ');
        const output = await this.sshService.sshExec(masterIp, command);
        try {
            return JSON.parse(output);
        }
        catch (err) {
            this.logger.error(`Failed to parse kubectl snapshot: ${err.message}`);
            throw new Error(`Cannot parse cluster state from master (${masterIp}). Output was not valid JSON.`);
        }
    }
    resolveApp(key, spec, ingresses, ingressRoutes, masterIp, nipHostnameToken) {
        const match = this.findIngressRouteMatch(spec, ingressRoutes) ??
            this.findIngressMatch(spec, ingresses);
        const baseDomain = (0, nip_base_domain_util_1.buildNipBaseDomain)(masterIp, nipHostnameToken);
        const defaultFqdn = spec.defaultSubdomain
            ? `${spec.defaultSubdomain}.${baseDomain}`
            : null;
        const defaultUrl = defaultFqdn
            ? `${spec.defaultScheme}://${defaultFqdn}`
            : '';
        if (match) {
            return {
                fqdn: match.host,
                defaultUrl,
                effectiveUrl: `${match.scheme}://${match.host}`,
                synced: true,
            };
        }
        return {
            fqdn: defaultFqdn,
            defaultUrl,
            effectiveUrl: defaultUrl,
            synced: !!defaultFqdn,
        };
    }
    findIngressMatch(spec, ingresses) {
        for (const ingress of ingresses) {
            const name = ingress.metadata?.name ?? '';
            const labelName = ingress.metadata?.labels?.['app.kubernetes.io/name'] ??
                ingress.metadata?.labels?.['app'] ??
                '';
            const matchesName = spec.ingressNames.includes(name);
            const matchesLabel = spec.ingressLabelNames.includes(labelName);
            if (!matchesName && !matchesLabel)
                continue;
            const host = ingress.spec?.rules?.find((r) => r.host)?.host;
            if (!host)
                continue;
            const tlsHosts = new Set((ingress.spec?.tls ?? []).flatMap((t) => t.hosts ?? []));
            const scheme = tlsHosts.has(host) ? 'https' : 'http';
            return { host, scheme };
        }
        return null;
    }
    findIngressRouteMatch(spec, routes) {
        for (const route of routes) {
            const name = route.metadata?.name ?? '';
            const labelName = route.metadata?.labels?.['app.kubernetes.io/name'] ??
                route.metadata?.labels?.['app'] ??
                '';
            const matchesName = spec.ingressNames.includes(name);
            const matchesLabel = spec.ingressLabelNames.includes(labelName);
            if (!matchesName && !matchesLabel)
                continue;
            const host = this.extractHostFromIngressRoute(route);
            if (!host)
                continue;
            const entryPoints = route.spec?.entryPoints ?? [];
            const hasTls = !!route.spec?.tls;
            const isSecure = hasTls || entryPoints.includes('websecure');
            const scheme = isSecure ? 'https' : 'http';
            return { host, scheme };
        }
        return null;
    }
    extractHostFromIngressRoute(route) {
        const hostRe = /Host\(\s*`([^`]+)`/;
        for (const r of route.spec?.routes ?? []) {
            const m = hostRe.exec(r.match ?? '');
            if (m)
                return m[1];
        }
        return null;
    }
};
exports.CliEndpointResolverService = CliEndpointResolverService;
exports.CliEndpointResolverService = CliEndpointResolverService = CliEndpointResolverService_1 = __decorate([
    (0, common_1.Injectable)(),
    __metadata("design:paramtypes", [cli_ssh_service_1.CliSshService])
], CliEndpointResolverService);
