"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.withRecorder = exports.ReplayEthTransport = exports.RecorderEthTransport = void 0;
const debug_logging_1 = require("@splunkdlt/debug-logging");
const fs_extra_1 = require("fs-extra");
const path_1 = require("path");
const { info } = debug_logging_1.createModuleDebug('recorder');
class RecorderEthTransport {
    constructor(delegate) {
        this.delegate = delegate;
        this.records = [];
    }
    get source() {
        return this.delegate.source;
    }
    record(request, response) {
        info('Recording eth request %s', request.method);
        const { id: _1, jsonrpc: _j1, ...req } = request;
        const { id: _2, jsonrpc: _j2, ...res } = response;
        this.records.push({ req, res });
    }
    async send(request) {
        const res = await this.delegate.send(request);
        this.record(request, res);
        return res;
    }
    async sendBatch(requests) {
        const responses = await this.delegate.sendBatch(requests);
        for (const req of requests) {
            const res = responses.find(r => r.id === req.id);
            if (res != null) {
                this.record(req, res);
            }
        }
        return responses;
    }
    async save(name, dir) {
        await fs_extra_1.mkdirp(dir);
        await fs_extra_1.writeFile(path_1.join(dir, `${name}.rec`), `[\n${this.records.map(r => JSON.stringify(r)).join(',\n')}\n]`, {
            encoding: 'utf-8'
        });
    }
}
exports.RecorderEthTransport = RecorderEthTransport;
class ReplayEthTransport {
    constructor(records, source) {
        this.records = records;
        this.source = source;
    }
    async send(request) {
        const paramsEqual = (a = [], b = []) => a.length === b.length && a.every((e, i) => b[i] === e);
        const reqMatches = (r) => r.req.method === request.method && paramsEqual(r.req.params, request.params);
        const recordIdx = this.records.findIndex(reqMatches);
        if (recordIdx < 0) {
            throw new Error(`Failed to replay request ${request.method}(${request.params.join(', ')}) - no matching recorded request found`);
        }
        const rec = this.records[recordIdx];
        const hasMoreMatchingRecords = this.records.some((r, i) => i > recordIdx && reqMatches(r));
        if (hasMoreMatchingRecords) {
            this.records.splice(recordIdx, 1);
        }
        return {
            jsonrpc: '2.0',
            id: request.id,
            ...rec.res
        };
    }
    async sendBatch(requests) {
        return Promise.all(requests.map(req => this.send(req)));
    }
}
exports.ReplayEthTransport = ReplayEthTransport;
async function withRecorder(delegate, options, task) {
    if (options.replay) {
        // todo
        const recordPath = path_1.join(options.storageDir, `${options.name}.rec`);
        const data = await fs_extra_1.readFile(recordPath, { encoding: 'utf-8' });
        const records = JSON.parse(data);
        const transport = new ReplayEthTransport(records, delegate.source);
        await task(transport);
    }
    else {
        const recorder = new RecorderEthTransport(delegate);
        await task(recorder);
        await recorder.save(options.name, options.storageDir);
    }
}
exports.withRecorder = withRecorder;
