"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const core_1 = require("@oclif/core");
const chalk_1 = __importDefault(require("chalk"));
const ora_1 = __importDefault(require("ora"));
const fs = __importStar(require("node:fs"));
const path = __importStar(require("node:path"));
const nest_app_1 = require("../../lib/nest-app");
const cli_observability_cluster_service_1 = require("../../services/cli-observability-cluster.service");
const cli_endpoint_resolver_service_1 = require("../../services/cli-endpoint-resolver.service");
const cli_ssh_service_1 = require("../../services/cli-ssh.service");
const config_storage_1 = require("../../lib/config-storage");
const cluster_entity_1 = require("../../../../src/modules/infrastructure/clusters/entities/cluster.entity");
const env_file_1 = require("../../lib/utils/env-file");
const context_banner_1 = require("../../lib/context-banner");
class EnvSync extends core_1.Command {
    async run() {
        const { flags } = await this.parse(EnvSync);
        (0, context_banner_1.printContextBanner)();
        const spinner = (0, ora_1.default)('Connecting to cluster master via SSH...').start();
        try {
            const app = await (0, nest_app_1.getNestApp)();
            const observabilityService = app.get(cli_observability_cluster_service_1.CliObservabilityClusterService);
            const resolver = app.get(cli_endpoint_resolver_service_1.CliEndpointResolverService);
            const sshService = app.get(cli_ssh_service_1.CliSshService);
            const cluster = await observabilityService.getObservabilityCluster();
            if (!cluster) {
                spinner.fail('No observability cluster found');
                console.log(chalk_1.default.yellow('\n⚠️  No observability cluster exists.\n'));
                console.log(chalk_1.default.dim('Create one with:'));
                console.log(`   ${chalk_1.default.cyan('flui env create')}\n`);
                return;
            }
            if (cluster.status !== cluster_entity_1.ClusterStatus.READY) {
                spinner.fail(`Cluster is not ready (status: ${cluster.status})`);
                return;
            }
            const masterIp = cluster.masterIpAddress;
            if (!masterIp) {
                spinner.fail('Master IP address not available');
                return;
            }
            const endpoints = await resolver.resolveEndpoints(masterIp, cluster.nipHostnameToken);
            spinner.succeed('Endpoints resolved from cluster');
            if (flags.debug) {
                console.log(chalk_1.default.cyan('\n🔬 Debug: ingresses on cluster\n'));
                try {
                    const raw = await sshService.sshExec(masterIp, "kubectl get ingress -A -o custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,HOSTS:.spec.rules[*].host,LABELS:.metadata.labels' 2>/dev/null || echo '(kubectl failed)'");
                    console.log(raw || chalk_1.default.dim('(no ingresses found)'));
                }
                catch (err) {
                    console.log(chalk_1.default.red(`Debug failed: ${err instanceof Error ? err.message : String(err)}`));
                }
                console.log();
            }
            const envVars = this.buildEnvVars(endpoints);
            this.displayEndpoints(cluster.name, masterIp, endpoints);
            const configStorage = new config_storage_1.ConfigStorage();
            const previousApiUrl = configStorage.getApiUrl();
            const nextApiUrl = this.deriveApiUrl(endpoints);
            this.displayDiff(previousApiUrl, nextApiUrl, envVars);
            if (flags['dry-run']) {
                console.log(chalk_1.default.yellow('\n🔍 Dry run mode — no changes applied.\n'));
                return;
            }
            this.writeEnvFile(envVars);
            configStorage.saveApiUrl(nextApiUrl);
            console.log(chalk_1.default.green('\n✅ Endpoints synced successfully.\n'));
        }
        catch (error) {
            spinner.fail('Failed to sync endpoints');
            console.error(chalk_1.default.red(`\n❌ ${error instanceof Error ? error.message : String(error)}\n`));
            console.log(chalk_1.default.dim('Troubleshooting: verify SSH access to the master node and that `kubectl` is available there.\n'));
            this.exit(1);
        }
        finally {
            await (0, nest_app_1.closeNestApp)();
        }
    }
    buildEnvVars(endpoints) {
        const envVars = {
            AUTH_MODE: endpoints.authMode,
            GRAFANA_URL: endpoints.grafana.effectiveUrl,
            PROMETHEUS_ENDPOINT: endpoints.prometheus.effectiveUrl,
            LOKI_ENDPOINT: endpoints.loki.effectiveUrl,
        };
        const issuer = this.resolveOidcIssuer(endpoints);
        if (issuer)
            envVars.OIDC_ISSUER = issuer;
        const jwks = this.resolveOidcJwksUri(endpoints, issuer);
        if (jwks)
            envVars.OIDC_JWKS_URI = jwks;
        if (endpoints.oidcAudience) {
            envVars.OIDC_AUDIENCE = endpoints.oidcAudience;
        }
        return envVars;
    }
    resolveOidcIssuer(endpoints) {
        // Prefer the Ingress (source of truth for the live domain) over the ConfigMap,
        // which can lag behind when the auth-domain-sync hasn't caught up.
        if (endpoints.zitadel.fqdn)
            return `https://${endpoints.zitadel.fqdn}`;
        if (endpoints.oidcIssuer)
            return endpoints.oidcIssuer;
        return '';
    }
    resolveOidcJwksUri(endpoints, issuer) {
        const raw = endpoints.oidcJwksUri;
        const isInCluster = raw.includes('.svc.cluster.local');
        if (raw && !isInCluster)
            return raw;
        if (issuer)
            return `${issuer.replace(/\/$/, '')}/oauth/v2/keys`;
        return '';
    }
    deriveApiUrl(endpoints) {
        const base = endpoints.fluiApi.effectiveUrl.replace(/\/$/, '');
        return base.endsWith('/api/v1') ? base : `${base}/api/v1`;
    }
    displayEndpoints(clusterName, masterIp, endpoints) {
        console.log(chalk_1.default.cyan('\n📋 Resolved Endpoints\n'));
        console.log(`   ${chalk_1.default.bold('Cluster:')}    ${clusterName}`);
        console.log(`   ${chalk_1.default.bold('Master IP:')}  ${masterIp}`);
        console.log(`   ${chalk_1.default.bold('Auth mode:')}  ${endpoints.authMode}\n`);
        const rows = [
            ['Flui API', endpoints.fluiApi],
            ['Flui Web', endpoints.fluiWeb],
            ['Zitadel', endpoints.zitadel],
            ['Grafana', endpoints.grafana],
            ['Prometheus', endpoints.prometheus],
            ['Loki', endpoints.loki],
        ];
        for (const [label, info] of rows) {
            const marker = info.synced ? chalk_1.default.green('●') : chalk_1.default.dim('○');
            const url = info.synced
                ? chalk_1.default.blue(info.effectiveUrl)
                : chalk_1.default.dim(info.effectiveUrl);
            console.log(`   ${marker} ${chalk_1.default.bold(label.padEnd(12))} ${url}`);
        }
    }
    displayDiff(previousApiUrl, nextApiUrl, envVars) {
        console.log(chalk_1.default.cyan('\n🔄 Changes to apply:\n'));
        if (previousApiUrl === nextApiUrl) {
            console.log(`   ${chalk_1.default.dim('apiUrl unchanged:')} ${previousApiUrl}`);
        }
        else {
            console.log(`   ${chalk_1.default.bold('apiUrl (config.json):')}`);
            console.log(`     ${chalk_1.default.red('- ' + previousApiUrl)}`);
            console.log(`     ${chalk_1.default.green('+ ' + nextApiUrl)}`);
        }
        console.log(`\n   ${chalk_1.default.bold('.env keys:')}`);
        for (const [k, v] of Object.entries(envVars)) {
            const shown = /SECRET|KEY|PASSWORD|AUDIENCE/i.test(k)
                ? chalk_1.default.yellow('******')
                : v;
            console.log(`     ${k}=${shown}`);
        }
    }
    writeEnvFile(envVars) {
        const envPath = path.join(process.cwd(), '.env');
        const envExamplePath = path.join(process.cwd(), '.env.example');
        let existing = '';
        if (fs.existsSync(envPath)) {
            existing = fs.readFileSync(envPath, 'utf-8');
        }
        else if (fs.existsSync(envExamplePath)) {
            existing = fs.readFileSync(envExamplePath, 'utf-8');
        }
        const updated = (0, env_file_1.updateEnvContent)(existing, envVars);
        fs.writeFileSync(envPath, updated, 'utf-8');
    }
}
EnvSync.description = 'Sync endpoint URLs (API, OIDC, observability) from the cluster via SSH+kubectl.\n' +
    'Safe for production: reads only K8s Ingress/ConfigMap/Secret, never touches DB/Redis credentials.';
EnvSync.examples = [
    '<%= config.bin %> <%= command.id %>',
    '<%= config.bin %> <%= command.id %> --dry-run',
];
EnvSync.flags = {
    'dry-run': core_1.Flags.boolean({
        description: 'Show what would change without modifying .env or config.json',
        default: false,
    }),
    debug: core_1.Flags.boolean({
        description: 'Print raw ingress list from the cluster for diagnostics',
        default: false,
    }),
};
exports.default = EnvSync;
