'use strict';

/**
 * Contract: endpoint list
 *
 * Menampilkan daftar endpoint dari metadata project di working directory
 * (`metadata/<project>.json`, output dari `endpoint create` / `dashboard create`).
 * File `global.json`, `*-workbench.json`, dan backup (`.backup.` / `.corrupt.`)
 * di-skip.
 *
 * Entry processor di-exclude: `processor create` juga menulis ke map `endpoints`
 * (via MetadataManager.updateEndpointMetadata, hardcode type 'module'), sehingga
 * nama yang terdaftar di map `processors` pada `<project>-workbench.json` bukan
 * endpoint dan hanya tampil di `processor list`.
 *
 * Kolom:
 * - Endpoint Name : key di `metadata.endpoints`
 * - Payload       : nama file `payload/<endpoint>.json` bila ada, `-` bila tidak
 * - Action        : daftar action bernilai true di metadata, `-` bila kosong
 *                   (mis. endpoint bertipe dashboard); di-wrap maksimal
 *                   4 action per baris, tanpa pemotongan
 *
 * Output default berupa table human-readable (`--format=text`). Output JSON
 * (untuk konsumsi AI agent / mcp-server) tersedia via `--format=json`.
 */

const fs = require('fs');
const path = require('path');
const ConfigReader = require('../../lib/config/config-reader');

/**
 * Baca daftar nama processor dari workbench metadata sebuah project,
 * untuk meng-exclude entry processor dari daftar endpoint.
 *
 * @param {string} metadataDir
 * @param {string} project
 * @returns {Set<string>}
 */
function readProcessorNames(metadataDir, project) {
    const workbenchPath = path.join(metadataDir, `${project}-workbench.json`);
    if (!fs.existsSync(workbenchPath)) {
        return new Set();
    }

    try {
        const workbench = JSON.parse(fs.readFileSync(workbenchPath, 'utf8'));
        if (workbench && typeof workbench.processors === 'object' && workbench.processors !== null) {
            return new Set(Object.keys(workbench.processors));
        }
    } catch (error) {
        console.warn(`Warning: skipping unreadable workbench metadata: ${project}-workbench.json (${error.message})`);
    }

    return new Set();
}

/**
 * Baca semua entry endpoint dari folder metadata/ di working directory.
 * Read-only: file metadata yang corrupt di-skip dengan warning, tidak ditulis ulang.
 *
 * @param {string} workingDir
 * @returns {{ project: string, name: string, payload: string|null, actions: string[] }[]}
 */
function readEndpointEntries(workingDir) {
    const metadataDir = path.join(workingDir, 'metadata');
    if (!fs.existsSync(metadataDir)) {
        return [];
    }

    const files = fs.readdirSync(metadataDir).filter(name =>
        name.endsWith('.json') &&
        name !== 'global.json' &&
        !name.endsWith('-workbench.json') &&
        !name.includes('.backup.') &&
        !name.includes('.corrupt.')
    );

    const entries = [];

    for (const file of files) {
        let metadata;
        try {
            metadata = JSON.parse(fs.readFileSync(path.join(metadataDir, file), 'utf8'));
        } catch (error) {
            console.warn(`Warning: skipping unreadable metadata file: ${file} (${error.message})`);
            continue;
        }

        if (!metadata || typeof metadata.endpoints !== 'object' || metadata.endpoints === null) {
            continue;
        }

        const project = metadata.moduleName || path.basename(file, '.json');
        const processorNames = readProcessorNames(metadataDir, project);

        for (const [endpointName, endpointData] of Object.entries(metadata.endpoints)) {
            if (processorNames.has(endpointName)) {
                continue;
            }

            const payloadFile = `${endpointName}.json`;
            const hasPayload = fs.existsSync(path.join(workingDir, 'payload', payloadFile));

            const actions = endpointData && endpointData.actions
                ? Object.entries(endpointData.actions)
                    .filter(([, enabled]) => enabled === true)
                    .map(([action]) => action)
                : [];

            entries.push({
                project,
                name: endpointName,
                payload: hasPayload ? payloadFile : null,
                actions
            });
        }
    }

    entries.sort((a, b) => a.name.localeCompare(b.name));
    return entries;
}

/**
 * Konversi pattern glob sederhana (`*` = karakter apa pun) menjadi RegExp
 * case-insensitive yang match keseluruhan nama. Tanpa wildcard berarti
 * exact match.
 * @param {string} pattern
 * @returns {RegExp}
 */
function patternToRegExp(pattern) {
    const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, '\\$&');
    return new RegExp('^' + escaped.replace(/\*/g, '.*') + '$', 'i');
}

/**
 * Potong string dengan suffix '..' bila melebihi lebar kolom (pola `project list`).
 * @param {string} value
 * @param {number} width
 * @returns {string}
 */
function truncate(value, width) {
    return value.length > width - 2
        ? value.substring(0, width - 4) + '..'
        : value;
}

/**
 * Render daftar endpoint ke format table human-readable.
 * @param {{ name: string, payload: string|null, actions: string[] }[]} entries
 * @returns {string}
 */
function renderHumanTable(entries) {
    const lines = [];

    const colName = 24;
    const colPayload = 28;
    const colAction = 44;

    lines.push('');
    lines.push('Registered Endpoints:');
    lines.push('');
    lines.push(
        'Endpoint Name'.padEnd(colName) +
        'Payload'.padEnd(colPayload) +
        'Action'.padEnd(colAction)
    );
    lines.push('-'.repeat(colName + colPayload + colAction));

    const ACTIONS_PER_LINE = 4;

    for (const entry of entries) {
        // Wrap action per 4 item; baris pertama menyatu dengan kolom lain,
        // baris lanjutan di-indent sejajar kolom Action.
        const actionLines = [];
        if (entry.actions.length === 0) {
            actionLines.push('-');
        } else {
            for (let i = 0; i < entry.actions.length; i += ACTIONS_PER_LINE) {
                const chunk = entry.actions.slice(i, i + ACTIONS_PER_LINE).join(', ');
                const isLast = i + ACTIONS_PER_LINE >= entry.actions.length;
                actionLines.push(isLast ? chunk : chunk + ',');
            }
        }

        lines.push(
            truncate(entry.name, colName).padEnd(colName) +
            truncate(entry.payload || '-', colPayload).padEnd(colPayload) +
            actionLines[0]
        );
        for (const continuation of actionLines.slice(1)) {
            lines.push(' '.repeat(colName + colPayload) + continuation);
        }
    }

    lines.push('');
    lines.push(`Total: ${entries.length} endpoint(s)`);
    lines.push('');

    return lines.join('\n');
}

module.exports = {
    resource: 'endpoint',
    verb: 'list',
    description: 'Menampilkan daftar endpoint dari metadata project di working directory',
    category: 'management',
    flags: {
        format: {
            type: 'string',
            required: false,
            default: 'text',
            description: 'Format output: `text` (table human-readable) atau `json` (untuk AI agent / mcp-server)'
        },
        name: {
            type: 'string',
            required: false,
            default: null,
            description: 'Filter nama endpoint, case-insensitive. Wildcard `*` = karakter apa pun (mis. `*product*`); tanpa wildcard berarti exact match'
        }
    },
    examples: [
        'npx restforge endpoint list',
        'npx restforge endpoint list --format=json',
        'npx restforge endpoint list --name=*product*'
    ],
    async handler(args) {
        const workingDir = ConfigReader.getWorkingDirectory();
        const allEntries = readEndpointEntries(workingDir);
        const format = (args.format || 'text').toLowerCase();

        const entries = args.name
            ? allEntries.filter(e => patternToRegExp(args.name).test(e.name))
            : allEntries;

        if (format === 'json') {
            const summary = {
                totalEndpoints: entries.length,
                projects: [...new Set(entries.map(e => e.project))].sort()
            };
            if (args.name) {
                summary.namePattern = args.name;
            }
            const output = {
                schemaVersion: '1.0',
                source: 'endpoint-list',
                summary,
                endpoints: entries
            };
            process.stdout.write(JSON.stringify(output, null, 2) + '\n');
            return;
        }

        if (allEntries.length === 0) {
            console.log('');
            console.log('No endpoints found in this project.');
            console.log('');
            console.log('Use "npx restforge endpoint create" to create a new endpoint.');
            console.log('');
            return;
        }

        process.stdout.write(renderHumanTable(entries) + '\n');
    }
};
