"use strict";
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 nest_app_1 = require("../../lib/nest-app");
const context_banner_1 = require("../../lib/context-banner");
const cli_observability_cluster_service_1 = require("../../services/cli-observability-cluster.service");
const cluster_node_scaling_service_1 = require("../../../../src/modules/infrastructure/clusters/services/cluster-node-scaling.service");
const cluster_capacity_service_1 = require("../../../../src/modules/infrastructure/clusters/services/cluster-capacity.service");
const cli_node_repository_1 = require("../../lib/repositories/cli-node.repository");
const cluster_node_entity_1 = require("../../../../src/modules/infrastructure/clusters/entities/cluster-node.entity");
const prompts_1 = require("../../lib/prompts");
class EnvScaleMaster extends core_1.Command {
    async run() {
        const { flags } = await this.parse(EnvScaleMaster);
        (0, context_banner_1.printContextBanner)();
        const spinner = (0, ora_1.default)('Preparing scale-master plan...').start();
        try {
            const app = await (0, nest_app_1.getNestApp)();
            const observabilityService = app.get(cli_observability_cluster_service_1.CliObservabilityClusterService);
            const nodeRepo = app.get(cli_node_repository_1.CliNodeRepository);
            const capacityService = app.get(cluster_capacity_service_1.ClusterCapacityService);
            const scalingService = app.get(cluster_node_scaling_service_1.ClusterNodeScalingService);
            const cluster = await observabilityService.getObservabilityCluster();
            if (!cluster) {
                spinner.fail('No observability cluster found');
                return;
            }
            const nodes = await nodeRepo.find({ where: { clusterId: cluster.id } });
            const master = nodes.find((n) => n.nodeType === cluster_node_entity_1.NodeType.MASTER);
            if (!master) {
                spinner.fail('Master node not found in this cluster');
                return;
            }
            const preview = await scalingService.previewScaleNode(cluster.id, master.id);
            // Capacity plan is best-effort: it depends on k8s API access which
            // may not be reachable from the workstation (the master k8s API is
            // typically firewalled to the cluster's source CIDR, or only reachable
            // via an SSH tunnel that becomes unreliable during a reboot). When the
            // plan fails, we still proceed with the explicit --type flag.
            let plan;
            try {
                plan = await capacityService.getPlan(cluster.id);
            }
            catch (err) {
                this.warn(`Capacity plan unavailable (${err.message}). ` +
                    'Proceeding without cost/candidate table.');
            }
            spinner.succeed('Plan computed');
            let targetType = flags.type;
            let cand = targetType
                ? plan?.candidates.find((c) => c.name === targetType)
                : undefined;
            if (!targetType) {
                if (!plan) {
                    console.log(chalk_1.default.red('\n❌ Cannot auto-pick a type without the capacity plan. Pass --type explicitly.\n'));
                    this.exit(1);
                }
                cand = plan.candidates.find((c) => c.direction === 'upgrade');
                if (!cand) {
                    console.log(chalk_1.default.red('\n❌ No upgrade candidate available from the provider catalog. Pass --type explicitly (e.g. for a downgrade).\n'));
                    this.exit(1);
                }
                targetType = cand.name;
                console.log(chalk_1.default.dim(`   (auto-selected next-bigger type: ${chalk_1.default.bold(targetType)})`));
            }
            console.log(chalk_1.default.cyan('\n🖥  Scale Master Plan\n'));
            console.log(`   ${chalk_1.default.bold('Cluster:')}     ${cluster.name}`);
            console.log(`   ${chalk_1.default.bold('Node:')}        ${preview.node.name}`);
            console.log(`   ${chalk_1.default.bold('From:')}        ${preview.node.currentServerType}`);
            console.log(`   ${chalk_1.default.bold('To:')}          ${targetType}`);
            if (cand) {
                let deltaStr;
                if (cand.monthlyDeltaEur === 'n/a')
                    deltaStr = 'n/a';
                else {
                    const sign = Number.parseFloat(cand.monthlyDeltaEur) > 0 ? '+' : '';
                    deltaStr = sign + cand.monthlyDeltaEur;
                }
                console.log(`   ${chalk_1.default.bold('Cost/month:')}  €${cand.monthlyCostEur} (${deltaStr} delta)`);
                console.log(`   ${chalk_1.default.bold('Resources:')}   ${cand.cores} CPU, ${cand.memoryGb} GB RAM, ${cand.diskGb} GB disk`);
            }
            else {
                console.log(chalk_1.default.dim(`   ${chalk_1.default.bold('Cost/month:')}  (unavailable)`));
            }
            console.log(`   ${chalk_1.default.bold('Downtime:')}    ~${Math.round(preview.expectedDowntimeMs / 60000)} min`);
            if (preview.affectedDedicatedApps.length > 0) {
                console.log(chalk_1.default.yellow(`\n   ⚠️  Affected dedicated workloads (${preview.affectedDedicatedApps.length}):`));
                for (const app of preview.affectedDedicatedApps) {
                    console.log(chalk_1.default.yellow(`     • ${app.slug}`));
                }
                console.log(chalk_1.default.yellow(`\n   These pods will be stopped for the duration.`));
                console.log(chalk_1.default.dim(`   Snapshot first with: flui app snapshot create <slug>\n`));
            }
            if (flags.upgradeDisk) {
                console.log(chalk_1.default.yellow('\n   ⚠️  --upgradeDisk is ONE-WAY. After this, you can never downgrade to a smaller type.\n'));
            }
            if (!flags.confirm) {
                console.log('');
                console.log(chalk_1.default.yellow(`   To confirm, type the cluster name exactly: ${chalk_1.default.bold(cluster.name)}`));
                const ok = await (0, prompts_1.confirmByTypingPrompt)(chalk_1.default.yellow('⚠️  Cluster name'), cluster.name);
                if (!ok) {
                    console.log(chalk_1.default.green('\n✅ Cancelled\n'));
                    return;
                }
            }
            const run = (0, ora_1.default)({
                text: `Scaling master to ${targetType}...`,
                color: 'yellow',
            }).start();
            try {
                const op = await scalingService.scaleNode(cluster.id, master.id, {
                    targetServerType: targetType,
                    upgradeDisk: flags.upgradeDisk,
                });
                run.succeed(`Master scaled. Operation ${op.id} → ${op.status}`);
            }
            catch (error) {
                run.fail('Scale-master failed');
                throw error;
            }
            console.log('');
        }
        catch (error) {
            console.log(chalk_1.default.red('\n❌ Error:\n'));
            console.log(`   ${error instanceof Error ? error.message : String(error)}\n`);
            this.exit(1);
        }
        finally {
            await (0, nest_app_1.closeNestApp)();
        }
    }
}
EnvScaleMaster.description = 'Vertically scale the master node to a new server type. Planned-maintenance operation: ' +
    'powers off the master, changes its provider server type, powers it back on and waits ' +
    'for k3s Ready. Expected downtime ~3–5 min. Any pod pinned to the master (dedicated DBs) ' +
    'will be unavailable during this window — snapshot them first.';
EnvScaleMaster.examples = [
    '<%= config.bin %> <%= command.id %> --type cx32',
    '<%= config.bin %> <%= command.id %> --type PRO2-S --confirm',
];
EnvScaleMaster.flags = {
    type: core_1.Flags.string({
        char: 't',
        description: 'Target provider server type name. If omitted, auto-picks the next-bigger upgrade candidate (smallest +€/month delta).',
    }),
    upgradeDisk: core_1.Flags.boolean({
        description: '[Hetzner] Also grow the local OS disk. One-way: you cannot downgrade later.',
        default: false,
    }),
    confirm: core_1.Flags.boolean({
        description: 'Skip the typed confirmation prompt',
        default: false,
    }),
};
exports.default = EnvScaleMaster;
