'use strict';

/**
 * Contract: init (verb global)
 *
 * Generate starter config file di working directory. Membuat
 * `config/<nama-file>.env` sebagai template starting point.
 *
 * Dua mode operasi:
 *   1. Interaktif (default, hanya saat stdin TTY dan TANPA --force):
 *      dialog konfirmasi gaya fast-track. Meminta input LICENSE, tipe database
 *      beserta atributnya, dan nama file config (default db-connection.env, bisa
 *      di-custom). Nilai input di-patch ke template lalu ditulis.
 *   2. Non-interaktif (saat --force diberikan ATAU stdin bukan TTY):
 *      menulis template apa adanya ke config/db-connection.env (perilaku legacy).
 *      Jalur ini dipakai oleh orkestrator (fast-track) dan playbook yang
 *      memanggil `npx restforge init --force`, sehingga harus tetap deterministik
 *      tanpa prompt.
 *
 * Catatan kompatibilitas: `--force` sengaja mempertahankan semantik non-interaktif
 * agar pemanggil existing (fast-track.js, restforge-playbook) tidak menggantung.
 * Tidak ada flag baru ditambahkan sehingga snapshot contract tidak berubah.
 *
 * Catatan: contract ini global verb (snapshot key = `init`, bukan
 * `<resource>/init`). Struktur file di `generators/cli/init.js` (root cli/
 * directory, bukan subdirectory resource).
 */

const path = require('node:path');
const fs = require('node:fs');
const readline = require('node:readline');
const { DB_CONNECTION_ENV_TEMPLATE: TEMPLATE_ENV } = require('../lib/templates/db-connection-env');

// ---------------------------------------------------------------------------
// Default konfigurasi (selaras dengan placeholder pada template db-connection.env)
// ---------------------------------------------------------------------------

const DEFAULT_CONFIG_FILE = 'db-connection.env';

const DB_DEFAULTS = {
    LICENSE: 'XXXX-XXXX-XXXX-XXXX',
    DB_TYPE: 'postgresql',
    DB_HOST: '127.0.0.1',
    DB_PORT: '5432',
    DB_USER: 'postgres',
    DB_PASSWORD: 'your_password_here',
    DB_NAME: 'your_database_name',
    DB_FILE: './data/myapp.db'
};

// Override DB_PORT/DB_USER/DB_NAME menyesuaikan DB_TYPE yang dipilih; bila tidak
// terdaftar, fallback ke DB_DEFAULTS (selaras dengan fast-track).
const DB_TYPE_DEFAULTS = {
    postgresql: { DB_PORT: '5432', DB_USER: 'postgres' },
    postgres:   { DB_PORT: '5432', DB_USER: 'postgres' },
    mysql:      { DB_PORT: '3306', DB_USER: 'root' },
    oracle:     { DB_PORT: '1521', DB_USER: 'oracle', DB_NAME: 'ORCL' }
};

const SUPPORTED_DB_TYPES = ['postgresql', 'mysql', 'oracle', 'sqlite'];

// ---------------------------------------------------------------------------
// Helper
// ---------------------------------------------------------------------------

/**
 * Prompter sederhana berbasis readline.question. init interaktif hanya berjalan
 * pada TTY (lihat handler), sehingga tidak butuh penanganan input pipe seperti
 * fast-track. `ask` dapat di-inject pada test via test seam.
 */
function createPrompter() {
    const rl = readline.createInterface({
        input: process.stdin,
        output: process.stdout
    });
    const ask = (message) => new Promise((resolve) => {
        rl.question(message, (answer) => resolve(answer));
    });
    return { ask, close: () => rl.close() };
}

/**
 * Masking license untuk tampilan review: grup pertama dan terakhir terlihat,
 * grup tengah disamarkan (mis. `8ECD-****-****-698Q`). Nilai asli tetap dipakai
 * untuk penulisan file.
 */
function maskLicense(license) {
    if (!license || typeof license !== 'string') return license;
    const groups = license.split('-');
    if (groups.length >= 3) {
        return groups
            .map((g, i) => (i === 0 || i === groups.length - 1) ? g : '*'.repeat(g.length))
            .join('-');
    }
    if (license.length > 8) {
        return `${license.slice(0, 4)}${'*'.repeat(license.length - 8)}${license.slice(-4)}`;
    }
    return license;
}

/** Default atribut database berdasarkan DB_TYPE (merge DB_DEFAULTS + override). */
function dbDefaultsFor(dbType) {
    const override = DB_TYPE_DEFAULTS[dbType] || {};
    return { ...DB_DEFAULTS, ...override };
}

/** Representasi database untuk baris review, berdasarkan konfigurasi input. */
function describeDatabase(cfg) {
    if (cfg.DB_TYPE === 'sqlite') return `sqlite @ ${cfg.DB_FILE}`;
    return `${cfg.DB_TYPE} @ ${cfg.DB_HOST}:${cfg.DB_PORT}/${cfg.DB_NAME}`;
}

/**
 * Fase input konfigurasi interaktif: LICENSE lalu tipe database beserta
 * atributnya. Mengembalikan objek cfg. `ask` di-inject agar dapat diuji.
 */
async function collectConfig(ask) {
    const askField = async (label, def) => {
        const input = (await ask(`  ${label} (${def}): `)).trim();
        return input || def;
    };

    console.log('');
    console.log('Enter configuration (press Enter to accept the default value):');
    console.log('');

    const cfg = {};

    // 1) LICENSE
    cfg.LICENSE = await askField('LICENSE', DB_DEFAULTS.LICENSE);

    // 2) Tipe database + atributnya
    console.log('');
    console.log(`  Supported DB_TYPE: ${SUPPORTED_DB_TYPES.join(', ')}`);
    cfg.DB_TYPE = (await askField('DB_TYPE', DB_DEFAULTS.DB_TYPE)).toLowerCase();

    if (cfg.DB_TYPE === 'sqlite') {
        console.log('');
        console.log('  SQLite mode: DB_HOST, DB_PORT, DB_USER, DB_PASSWORD are ignored.');
        console.log('  The database file path is set in DB_NAME (via DB_FILE).');
        console.log('');
        cfg.DB_FILE = await askField('DB_FILE (.db file path)', DB_DEFAULTS.DB_FILE);
    } else {
        const d = dbDefaultsFor(cfg.DB_TYPE);
        cfg.DB_HOST = await askField('DB_HOST', d.DB_HOST);
        cfg.DB_PORT = await askField('DB_PORT', d.DB_PORT);
        cfg.DB_USER = await askField('DB_USER', d.DB_USER);
        cfg.DB_PASSWORD = await askField('DB_PASSWORD', d.DB_PASSWORD);
        cfg.DB_NAME = await askField('DB_NAME', d.DB_NAME);
    }

    return cfg;
}

/** Susun map nilai env yang akan di-patch ke template dari konfigurasi input. */
function envValuesFromCfg(cfg) {
    const v = {
        LICENSE: cfg.LICENSE,
        DB_TYPE: cfg.DB_TYPE
    };
    if (cfg.DB_TYPE === 'sqlite') {
        // Untuk SQLite, path file disimpan pada DB_NAME (dikonsumsi runtime).
        v.DB_NAME = cfg.DB_FILE;
    } else {
        v.DB_HOST = cfg.DB_HOST;
        v.DB_PORT = cfg.DB_PORT;
        v.DB_USER = cfg.DB_USER;
        v.DB_PASSWORD = cfg.DB_PASSWORD;
        v.DB_NAME = cfg.DB_NAME;
    }
    return v;
}

/**
 * Patch VALUE sejumlah KEY pada teks template tanpa mengubah baris lain
 * (komentar, section Redis/Kafka/dll. tetap utuh). KEY yang belum ada di
 * template ditambahkan di akhir.
 */
function applyConfigToTemplate(template, values) {
    const eol = template.includes('\r\n') ? '\r\n' : '\n';
    const lines = template.split(/\r?\n/);
    const remaining = new Set(Object.keys(values));
    for (let i = 0; i < lines.length; i++) {
        const m = lines[i].match(/^(\s*)([A-Za-z_][A-Za-z0-9_]*)\s*=/);
        if (m && remaining.has(m[2])) {
            lines[i] = `${m[1]}${m[2]}=${values[m[2]]}`;
            remaining.delete(m[2]);
        }
    }
    for (const k of remaining) lines.push(`${k}=${values[k]}`);
    return lines.join(eol);
}

/** Tulis file config ke config/<configFileName>, buat folder bila perlu. */
function writeConfigFile(workingDir, configFileName, content) {
    const configDir = path.resolve(workingDir, 'config');
    if (!fs.existsSync(configDir)) {
        fs.mkdirSync(configDir, { recursive: true });
    }
    const absolutePath = path.resolve(configDir, configFileName);
    fs.writeFileSync(absolutePath, content, 'utf8');
    console.log(`  Created: ${path.join('config', configFileName)}`);
}

/** Cetak ringkasan akhir + next steps. */
function printDone(configFileName) {
    console.log('');
    console.log('Done! Sample files generated successfully.');
    console.log('');
    console.log('Next Steps:');
    console.log('');
    console.log(`  1. Edit config/${configFileName}`);
    console.log('     Update database credentials (DB_HOST, DB_USER, DB_PASSWORD, DB_NAME)');
    console.log('');
}

/** Review + konfirmasi sebelum menulis (mode interaktif). */
async function confirmWrite(ctx, ask) {
    console.log('');
    console.log('====================================================');
    console.log('  Review configuration');
    console.log('====================================================');
    console.log(`  License   : ${maskLicense(ctx.cfg.LICENSE)}`);
    console.log(`  Database  : ${describeDatabase(ctx.cfg)}`);
    const existsNote = ctx.exists ? '   (EXISTING — will be overwritten)' : '';
    console.log(`  Target    : config/${ctx.configFileName}${existsNote}`);
    console.log('');
    const answer = (await ask('  Write this config? (Y/n): ')).trim().toLowerCase();
    return !(answer === 'n' || answer === 'no');
}

/**
 * Jalur non-interaktif: tulis template apa adanya ke config/db-connection.env.
 * Mempertahankan perilaku legacy (termasuk guard file existing tanpa --force).
 */
function runNonInteractive(workingDir, force) {
    const configFileName = DEFAULT_CONFIG_FILE;
    const relativePath = path.join('config', configFileName);
    const absolutePath = path.resolve(workingDir, relativePath);

    if (fs.existsSync(absolutePath) && !force) {
        console.log('WARNING: The following files already exist:');
        console.log('');
        console.log(`  ${relativePath}`);
        console.log('');
        console.log('Use --force to overwrite:');
        console.log('  npx restforge init --force');
        console.log('');
        throw new Error('1 file(s) already exist. Use --force to overwrite.');
    }

    writeConfigFile(workingDir, configFileName, TEMPLATE_ENV);
    printDone(configFileName);
}

/** Jalur interaktif: kumpulkan input, konfirmasi, lalu tulis template ter-patch. */
async function runInteractive(workingDir, prompter) {
    const { ask } = prompter;

    // 1) LICENSE + 2) tipe database & atributnya.
    const cfg = await collectConfig(ask);

    // 3) Nama file config (terakhir; default db-connection.env, bisa di-custom).
    console.log('');
    const fnameInput = (await ask(`  Config file name (${DEFAULT_CONFIG_FILE}): `)).trim();
    // basename untuk mencegah path traversal; nama selalu relatif ke config/.
    const configFileName = path.basename(fnameInput || DEFAULT_CONFIG_FILE);

    const absolutePath = path.resolve(workingDir, 'config', configFileName);
    const exists = fs.existsSync(absolutePath);

    const ok = await confirmWrite({ cfg, configFileName, exists }, ask);
    if (!ok) {
        console.log('');
        console.log('  Aborted. No changes were made.');
        return;
    }

    const content = applyConfigToTemplate(TEMPLATE_ENV, envValuesFromCfg(cfg));
    console.log('');
    writeConfigFile(workingDir, configFileName, content);
    printDone(configFileName);
}

// ---------------------------------------------------------------------------
// Contract
// ---------------------------------------------------------------------------

module.exports = {
    verb: 'init',
    description: 'Generate starter config file (config/db-connection.env) di working directory. Interaktif di terminal; non-interaktif dengan --force',
    category: 'utility',
    flags: {
        force: {
            type: 'boolean',
            required: false,
            default: false,
            description: 'Timpa file existing tanpa konfirmasi'
        }
    },
    examples: [
        'npx restforge init',
        'npx restforge init --force'
    ],
    async handler(args) {
        const workingDir = process.cwd();

        console.log('');
        console.log('RESTForge - Init');
        console.log('====================');

        // Interaktif hanya bila TTY dan TANPA --force. `--force` dan konteks
        // non-TTY (pipe/CI/spawn dari orkestrator) memakai jalur non-interaktif
        // deterministik agar tidak menggantung menunggu input.
        const interactive = args.force !== true && Boolean(process.stdin.isTTY);

        if (!interactive) {
            runNonInteractive(workingDir, args.force === true);
            return;
        }

        const prompter = createPrompter();
        try {
            await runInteractive(workingDir, prompter);
        } finally {
            prompter.close();
        }
    }
};

// Test seam (hanya aktif via env): mengekspos helper internal agar alur
// interaktif dapat diuji dengan `ask` ter-inject tanpa stdin TTY. Pada
// penggunaan CLI normal env ini tidak di-set sehingga export tetap contract murni.
if (process.env.INIT_TEST === '1') {
    module.exports.__test = {
        collectConfig,
        applyConfigToTemplate,
        envValuesFromCfg,
        dbDefaultsFor,
        describeDatabase,
        maskLicense,
        confirmWrite,
        runInteractive,
        DEFAULT_CONFIG_FILE,
        SUPPORTED_DB_TYPES
    };
}
