#!/usr/bin/env node

/**
 * Rainbow CLI — MCP Server
 *
 * Expose les capacités du CLI Rainbow comme outils MCP (Model Context Protocol)
 * pour qu'un LLM puisse interroger la plateforme Rainbow CPaaS en langage naturel.
 *
 * Transport : stdio (standard MCP local)
 * Usage     : node mcp-server.js
 * Config    : utilise le profil rainbow-cli-ng actif (~/.rainbow-cli-config.json + keyring OS)
 */

import './util/file-polyfill.js';

import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';

import { Logger } from './util/logger.js';
import { ConfigManager } from './config/configManager.js';
import { Rest } from './rest.js';
import { Admin } from './services/admin.js';
import { Applications } from './services/applications.js';
import { EndUser } from './services/enduser.js';
import { Subscription } from './services/subscription.js';

// ---------------------------------------------------------------------------
// Shim Commander.js — les constructeurs de service enregistrent des commandes
// CLI via program.command(...).addOption(...).action(...). On fournit un objet
// qui absorbe tous ces appels sans rien faire, pour pouvoir réutiliser les
// services directement sans passer par le framework CLI.
// ---------------------------------------------------------------------------

const noopChain = new Proxy(Object.create(null), {
    get(_, prop) {
        if (prop === 'commands') return [];
        if (prop === 'opts')     return () => ({});
        if (typeof prop === 'symbol') return undefined;
        return () => noopChain;
    }
});

// ---------------------------------------------------------------------------
// Initialisation des services
// ---------------------------------------------------------------------------

async function initServices() {
    const logger = new Logger();
    const services = [];

    // Stub telemetry (non nécessaire hors CLI)
    services['telemetry'] = { sendTelemetry: () => {} };

    const configManager = new ConfigManager(noopChain, logger, services);
    await configManager.load();

    new Rest(noopChain, logger, services);
    new Admin(noopChain, logger, services);
    new Applications(noopChain, logger, services);
    new EndUser(noopChain, logger, services);
    new Subscription(noopChain, logger, services);

    return services;
}

// ---------------------------------------------------------------------------
// Définition des outils MCP
// ---------------------------------------------------------------------------

const TOOLS = [
    {
        name: 'whoami',
        description:
            'Get information about the currently connected Rainbow user ' +
            '(id, loginEmail, displayName, companyId, roles).',
        inputSchema: { type: 'object', properties: {} }
    },
    {
        name: 'list_users',
        description:
            'List Rainbow users. Returns id, loginEmail, displayName, companyId, roles for each user.',
        inputSchema: {
            type: 'object',
            properties: {
                name:      { type: 'string',  description: 'Filter by display name (partial match)' },
                email:     { type: 'string',  description: 'Filter by login email' },
                companyId: { type: 'string',  description: 'Filter by company id' },
                role:      { type: 'string',  description: 'Filter by role (e.g. app_admin, admin)' },
                limit:     { type: 'number',  description: 'Max results to return (default: 25, max: 1000)' }
            }
        }
    },
    {
        name: 'get_user',
        description: 'Get full details of a Rainbow user by id.',
        inputSchema: {
            type: 'object',
            properties: {
                userId: { type: 'string', description: 'User unique identifier' }
            },
            required: ['userId']
        }
    },
    {
        name: 'list_companies',
        description: 'List Rainbow companies.',
        inputSchema: {
            type: 'object',
            properties: {
                name:  { type: 'string', description: 'Filter by company name (partial match)' },
                limit: { type: 'number', description: 'Max results to return' }
            }
        }
    },
    {
        name: 'get_company',
        description: 'Get full details of a Rainbow company by id.',
        inputSchema: {
            type: 'object',
            properties: {
                companyId: { type: 'string', description: 'Company unique identifier' }
            },
            required: ['companyId']
        }
    },
    {
        name: 'list_applications',
        description:
            'List CPaaS applications. ' +
            'The "kpi" field defines the commercial offer: ' +
            '"payasyougo" = self-service, billed per API consumption via Zuora/Stripe (requires a payment method); ' +
            '"business" = monthly ALE contract, billing handled outside Rainbow; ' +
            '"appconnect" = requires a Rainbow App Connect subscription (per-user monthly); ' +
            '"internal" = ALE internal developers only (@al-enterprise.com); ' +
            '"demo" = free trial. ' +
            'Only payasyougo applications are linked to developer payment methods.',
        inputSchema: {
            type: 'object',
            properties: {
                offer: {
                    type: 'string',
                    enum: ['payasyougo', 'business', 'appconnect', 'internal', 'demo'],
                    description: 'Filter by commercial offer type (maps to kpi field)'
                },
                ownerId: { type: 'string',  description: 'Filter by owner user id' },
                env: {
                    type: 'string',
                    enum: ['not_deployed', 'in_deployment', 'deployed'],
                    description: 'Filter by deployment state'
                },
                active:   { type: 'boolean', description: 'Filter: only active applications' },
                limit:    { type: 'number',  description: 'Max results to return' }
            }
        }
    },
    {
        name: 'get_application',
        description: 'Get full details of a CPaaS application by id.',
        inputSchema: {
            type: 'object',
            properties: {
                appId: { type: 'string', description: 'Application unique identifier' }
            },
            required: ['appId']
        }
    },
    {
        name: 'get_developer_subscriptions',
        description:
            'Get CPaaS subscriptions for a developer user. ' +
            'Each subscription links one of their applications to a CPaaS offer in the billing system (Zuora). ' +
            'Returns offerId, offerName, status, applicationId.',
        inputSchema: {
            type: 'object',
            properties: {
                userId: { type: 'string', description: 'Developer user id' }
            },
            required: ['userId']
        }
    },
    {
        name: 'get_developer_payment_methods',
        description:
            'Get payment methods configured by a developer on Rainbow for Developers portal. ' +
            'Only relevant for payasyougo applications — the developer must have at least one ' +
            'active payment method for their PAYG app to be billed correctly. ' +
            'Returns card type, masked number, holder name, expiry, status.',
        inputSchema: {
            type: 'object',
            properties: {
                userId: { type: 'string', description: 'Developer user id' }
            },
            required: ['userId']
        }
    }
];

// ---------------------------------------------------------------------------
// Gestionnaires d'outils
// ---------------------------------------------------------------------------

function formatResult(data) {
    return {
        content: [{
            type: 'text',
            text: JSON.stringify(data, null, 2)
        }]
    };
}

function formatError(err) {
    const msg = err?.message || String(err);
    return {
        content: [{ type: 'text', text: `Error: ${msg}` }],
        isError: true
    };
}

async function callTool(name, args, services) {
    const admin        = services['admin'];
    const applications = services['applications'];
    const enduser      = services['enduser'];
    const subscription = services['subscription'];

    switch (name) {
        case 'whoami':
            return formatResult(await enduser.whoami());

        case 'list_users': {
            const opts = {};
            if (args.name)      opts.name  = args.name;
            if (args.email)     opts.email = args.email;
            if (args.companyId) opts.cid   = args.companyId;
            if (args.role)      opts.role  = args.role;
            if (args.limit)     opts.limit = args.limit;
            return formatResult(await admin.getUsers(opts));
        }

        case 'get_user':
            return formatResult(await admin.getUser(args.userId, {}));

        case 'list_companies': {
            const opts = {};
            if (args.name)  opts.name  = args.name;
            if (args.limit) opts.limit = args.limit;
            return formatResult(await admin.getCompanies(opts));
        }

        case 'get_company':
            return formatResult(await admin.getCompany(args.companyId, {}));

        case 'list_applications': {
            const opts = { format: 'full' };
            if (args.offer)   opts.offer  = args.offer;
            if (args.ownerId) opts.owner  = args.ownerId;
            if (args.env)     opts[args.env === 'deployed' ? 'deployed' :
                                   args.env === 'in_deployment' ? 'inDeployment' : 'notDeployed'] = true;
            if (args.active)  opts.active = true;
            if (args.limit)   opts.limit  = args.limit;
            return formatResult(await applications.getApplications(opts));
        }

        case 'get_application':
            return formatResult(await applications.getApplication(args.appId, { format: 'full' }));

        case 'get_developer_subscriptions':
            return formatResult(await subscription.getDeveloperSubscriptions(args.userId));

        case 'get_developer_payment_methods':
            return formatResult(await subscription.getPaymentMethods(args.userId));

        default:
            throw new Error(`Unknown tool: ${name}`);
    }
}

// ---------------------------------------------------------------------------
// Main
// ---------------------------------------------------------------------------

async function main() {
    const services = await initServices();

    const server = new Server(
        { name: 'rainbow-cli', version: '1.0.0' },
        { capabilities: { tools: {} } }
    );

    server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS }));

    server.setRequestHandler(CallToolRequestSchema, async (request) => {
        const { name, arguments: args = {} } = request.params;
        try {
            return await callTool(name, args, services);
        } catch (err) {
            return formatError(err);
        }
    });

    const transport = new StdioServerTransport();
    await server.connect(transport);
}

main().catch(err => {
    process.stderr.write(`MCP server fatal error: ${err.message || err}\n`);
    process.exit(1);
});
