import getPort from 'get-port';
import express from 'express';
import cors from 'cors';
import fs from 'fs/promises';
import path from 'path';
import http from 'http';
import { WebSocket, WebSocketServer } from 'ws';
import { spawn, spawnSync } from 'child_process';
import { Command, Args, Flags } from '@oclif/core';
import { IS_DEBUGGING, CORE_PATH, STANDALONE_PATH, CHECKOUTRC_PATH, STATIC_TMP_FOLDER, } from '../lib/constants.js';
import { createProxyMiddleware } from 'http-proxy-middleware';
import { bundle } from '../lib/bundler.js';
import { logger } from '../lib/logger.js';
const isCorePristine = async () => {
    const content = await fs.readFile(CHECKOUTRC_PATH);
    return content.toString().trim() === 'pristine';
};
const getPackageManager = () => {
    const agent = spawnSync('na', ['?'], {
        encoding: 'utf8',
        shell: true,
    }).stdout.trim();
    if (agent === '')
        return 'npm';
    return agent;
};
const setupCheckoutCore = async () => {
    const isPristine = await isCorePristine();
    if (isPristine) {
        const packageManager = getPackageManager();
        logger.info('Installing internal dependencies, may take a while 🕑');
        logger.debug('Installing dependencies using: ', packageManager);
        spawnSync('sh', ['-c', `cd ${STANDALONE_PATH} && ${packageManager} install`], {
            shell: false,
            stdio: IS_DEBUGGING ? 'inherit' : 'ignore',
        });
        await fs.writeFile(CHECKOUTRC_PATH, 'updated');
    }
};
const createExtensionsServer = async ({ port, accountName, path: checkoutPath, showPlaceholders, }) => {
    const app = express();
    app.use(cors({
        origin: `http://localhost:${port}`,
        credentials: true,
    }));
    const server = http.createServer(app);
    const wss = new WebSocketServer({ server });
    const EXTENSIONS_SERVER_PORT = await getPort();
    const clients = new Set();
    wss.on('connection', (ws) => {
        logger.debug('New WS client connected');
        clients.add(ws);
        ws.on('close', () => {
            logger.debug('Client disconnected');
            clients.delete(ws);
        });
    });
    app.get('/extensions-config', (_, res) => res.json({
        accountName,
        extensionsServerPort: EXTENSIONS_SERVER_PORT,
        showPlaceholders,
    }));
    app.post('/graphql', createProxyMiddleware({
        target: `https://${accountName}.vtexcommercestable.com.br/api/checkout-bff/graphql`,
        changeOrigin: true,
        cookieDomainRewrite: 'localhost',
        cookiePathRewrite: '/',
        pathRewrite: {
            '^/graphql': '',
        },
        ...(IS_DEBUGGING && { logger: console }),
    }));
    app.use('/assets', express.static(path.resolve(checkoutPath, STATIC_TMP_FOLDER)));
    server.listen(EXTENSIONS_SERVER_PORT, () => {
        logger.debug(`Extensions server running at http://localhost:${EXTENSIONS_SERVER_PORT} `);
        if (showPlaceholders) {
            logger.debug('ℹ️  Displaying placeholders at all extension points. Extensions will not be displayed.');
        }
    });
    const checkoutServer = path.resolve(CORE_PATH, 'standalone', 'server.js');
    const child = spawn(process.execPath, [checkoutServer], {
        stdio: IS_DEBUGGING ? 'inherit' : 'ignore',
        env: {
            PORT: port,
            CHECKOUT_CLI_EXTENSIONS_SERVER_PORT: EXTENSIONS_SERVER_PORT.toString(),
        },
        shell: true,
    });
    child.on('close', (code) => {
        logger.debug(`Next.js server exited with code: ${code}`);
    });
    return {
        emit: () => {
            clients.forEach((client) => {
                if (client.readyState === WebSocket.OPEN)
                    client.send('reload');
            });
        },
    };
};
export class Dev extends Command {
    static args = {
        path: Args.string({
            description: 'The path where the extensions are located.',
            required: true,
        }),
        accountName: Args.string({
            description: 'The account name of the store.',
            required: true,
        }),
    };
    static description = 'Run checkout in development mode';
    static examples = [];
    static flags = {
        'show-placeholders': Flags.boolean({
            description: 'Show placeholders for extension points',
            default: false,
        }),
    };
    /**
     * TODO: Currently, we're getting PATH, ACCOUNT_NAME, and PORT
     * via CLI arguments. However, this information will later come from
     * the FastStore CLI when the developer runs a command like:
     * faststore dev <account-name>. The remaining information will be
     * sourced from the faststore.config.json file.
     */ async run() {
        const CHECKOUT_PORT = '4301';
        const { args, flags } = await this.parse(Dev);
        const checkoutPath = path.resolve(args.path);
        await setupCheckoutCore();
        const extensionsServer = await createExtensionsServer({
            port: CHECKOUT_PORT,
            accountName: args.accountName,
            path: checkoutPath,
            showPlaceholders: flags['show-placeholders'],
        });
        bundle({
            mode: 'development',
            path: checkoutPath,
            onChange: () => {
                logger.debug('Reloading Checkout UI');
                extensionsServer.emit('reload');
            },
        });
    }
}
//# sourceMappingURL=dev.js.map