"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 readline = __importStar(require("readline"));
const nest_app_1 = require("../../lib/nest-app");
const cli_observability_cluster_service_1 = require("../../services/cli-observability-cluster.service");
const cli_ssh_service_1 = require("../../services/cli-ssh.service");
const cluster_entity_1 = require("../../../../src/modules/infrastructure/clusters/entities/cluster.entity");
const DEFAULT_EMAIL = 'admin@flui.cloud';
class AuthResetPassword extends core_1.Command {
    async run() {
        const { flags } = await this.parse(AuthResetPassword);
        const spinner = (0, ora_1.default)('Connecting to observability cluster...').start();
        try {
            const app = await (0, nest_app_1.getNestApp)();
            const observabilityService = app.get(cli_observability_cluster_service_1.CliObservabilityClusterService);
            const sshService = app.get(cli_ssh_service_1.CliSshService);
            // Get observability cluster
            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'));
                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;
            }
            // Verify auth mode is local
            spinner.text = 'Checking auth mode...';
            let authMode = 'unknown';
            try {
                authMode = await sshService.sshExec(masterIp, "kubectl get configmap flui-api-config -n flui-system -o jsonpath='{.data.AUTH_MODE}' 2>/dev/null || echo 'unknown'");
                authMode = authMode.trim();
            }
            catch {
                authMode = 'unknown';
            }
            if (authMode !== 'local') {
                spinner.fail(`Auth mode is "${authMode}", not "local"`);
                console.log(chalk_1.default.yellow('\n⚠️  auth:reset-password only works in local auth mode.\n'));
                console.log(chalk_1.default.dim('In OIDC mode, reset passwords through your identity provider.\n'));
                return;
            }
            spinner.succeed(`Cluster ready (auth mode: ${authMode})`);
            const email = flags.email;
            const password = flags.password ?? this.generatePassword();
            const autoGenerated = !flags.password;
            // Confirm
            if (!flags.force) {
                const confirmed = await this.confirm(`Reset password for ${chalk_1.default.bold(email)}? This will also invalidate all active sessions.`);
                if (!confirmed) {
                    console.log(chalk_1.default.dim('\nCancelled.\n'));
                    return;
                }
            }
            // Execute password reset in the flui-api pod via SSH + kubectl exec
            const resetSpinner = (0, ora_1.default)('Resetting password...').start();
            // Node.js script that:
            // 1. Hashes the password with bcrypt
            // 2. Updates the users table
            // 3. Deletes all refresh tokens for the user
            const script = `
const bcrypt = require('bcrypt');
const { Client } = require('pg');

async function main() {
  const email = ${JSON.stringify(email)};
  const password = ${JSON.stringify(password)};

  const hash = await bcrypt.hash(password, 12);

  const pg = new Client({
    host: process.env.DB_HOST || 'localhost',
    port: parseInt(process.env.DB_PORT || '5432', 10),
    user: process.env.DB_USERNAME || 'fluicloud',
    password: process.env.DB_PASSWORD || '',
    database: process.env.DB_NAME || 'fluicloud',
  });

  await pg.connect();

  const res = await pg.query(
    'UPDATE users SET "passwordHash"=$1 WHERE email=$2 RETURNING id',
    [hash, email],
  );

  if (res.rowCount === 0) {
    await pg.end();
    console.error('USER_NOT_FOUND');
    process.exit(1);
  }

  const userId = res.rows[0].id;
  await pg.query('DELETE FROM refresh_tokens WHERE "userId"=$1', [userId]);
  await pg.end();
  console.log('OK:' + userId);
}

main().catch((err) => { console.error(err.message); process.exit(1); });
`.trim();
            const encoded = Buffer.from(script).toString('base64');
            // Find the flui-api pod name
            let podName;
            try {
                podName = (await sshService.sshExec(masterIp, "kubectl get pods -n default -l app=flui-api -o jsonpath='{.items[0].metadata.name}' 2>/dev/null")).trim();
            }
            catch {
                podName = '';
            }
            if (!podName) {
                resetSpinner.fail('flui-api pod not found');
                console.log(chalk_1.default.red('\n❌ Could not find flui-api pod. Is the deployment running?\n'));
                return;
            }
            // Execute script inside the pod
            let result;
            try {
                result = await sshService.sshExec(masterIp, `kubectl exec -n default ${podName} -- node -e "eval(Buffer.from('${encoded}','base64').toString())"`);
            }
            catch (err) {
                resetSpinner.fail('Password reset failed');
                console.log(chalk_1.default.red(`\n❌ ${err.message}\n`));
                return;
            }
            result = result.trim();
            if (result.includes('USER_NOT_FOUND')) {
                resetSpinner.fail(`User not found: ${email}`);
                console.log(chalk_1.default.yellow(`\n⚠️  No user with email "${email}" exists.\n`));
                return;
            }
            if (!result.startsWith('OK:')) {
                resetSpinner.fail('Unexpected response from reset script');
                console.log(chalk_1.default.red(`\n❌ Response: ${result}\n`));
                return;
            }
            resetSpinner.succeed('Password reset successfully');
            console.log(chalk_1.default.green('\n✅ Password reset complete\n'));
            console.log(`   ${chalk_1.default.bold('Email:')} ${email}`);
            if (autoGenerated) {
                console.log(`   ${chalk_1.default.bold('Password:')} ${chalk_1.default.yellow(password)}  ${chalk_1.default.red('← save this, shown only once')}`);
            }
            else {
                console.log(`   ${chalk_1.default.bold('Password:')} ${chalk_1.default.dim('(as provided)')}`);
            }
            console.log(chalk_1.default.dim('\n   All active sessions for this user have been invalidated.\n'));
        }
        catch (error) {
            spinner.fail('Error during password reset');
            console.error(chalk_1.default.red(`\n❌ ${error.message}\n`));
            this.exit(1);
        }
        finally {
            await (0, nest_app_1.closeNestApp)();
        }
    }
    /** Prompt user for y/N confirmation */
    confirm(message) {
        return new Promise((resolve) => {
            const rl = readline.createInterface({
                input: process.stdin,
                output: process.stdout,
            });
            rl.question(`${message} [y/N] `, (answer) => {
                rl.close();
                resolve(answer.trim().toLowerCase() === 'y');
            });
        });
    }
    /** Generate a random 20-character password */
    generatePassword() {
        const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz23456789!@#$%^&*';
        let result = '';
        const array = new Uint8Array(20);
        // Use crypto.getRandomValues if available, otherwise Math.random fallback
        if (typeof globalThis.crypto?.getRandomValues === 'function') {
            globalThis.crypto.getRandomValues(array);
            for (const byte of array) {
                result += chars[byte % chars.length];
            }
        }
        else {
            for (let i = 0; i < 20; i++) {
                result += chars[Math.floor(Math.random() * chars.length)];
            }
        }
        return result;
    }
}
AuthResetPassword.description = 'Reset admin password (local auth mode only). ' +
    'Connects via SSH to the observability cluster and updates the password in the database.';
AuthResetPassword.examples = [
    '<%= config.bin %> <%= command.id %>',
    '<%= config.bin %> <%= command.id %> --email admin@flui.cloud',
    '<%= config.bin %> <%= command.id %> --email admin@flui.cloud --password "MyNewPass123!"',
];
AuthResetPassword.flags = {
    email: core_1.Flags.string({
        description: 'Admin email address',
        default: DEFAULT_EMAIL,
    }),
    password: core_1.Flags.string({
        description: 'New password (if omitted, a secure password is auto-generated and printed once)',
    }),
    force: core_1.Flags.boolean({
        description: 'Skip confirmation prompt',
        default: false,
    }),
};
exports.default = AuthResetPassword;
