"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Strategy = void 0;
const tslib_1 = require("tslib");
const fs = (0, tslib_1.__importStar)(require("node:fs"));
const loader_1 = (0, tslib_1.__importDefault)(require("@assemblyscript/loader"));
const ajv_1 = (0, tslib_1.__importDefault)(require("ajv"));
function toArrayBuffer(buf) {
    const ab = new ArrayBuffer(buf.length);
    const view = new Uint8Array(ab);
    for (const [i, element] of buf.entries()) {
        view[i] = element;
    }
    return ab;
}
class Strategy {
    constructor(strategyPath) {
        this.results = [];
        this.configParams = {};
        this.candles = [];
        this.numCandles = 15;
        this.filePath = strategyPath;
        this.config = {};
    }
    async load() {
        const importObject = {
            env: {
                abort(msg, file, line, column) {
                    console.error('abort called at ' + file + ' line:' + line + ':' + column, 'msg: ' + msg);
                },
            },
        };
        const file = fs.readFileSync(this.filePath);
        const instance = await loader_1.default.instantiate(toArrayBuffer(file), importObject);
        this.module = instance.exports;
        this.config = await this._getConfig();
        return instance;
    }
    setNumCandles(num) {
        this.numCandles = num;
    }
    // public async execute(data: StrategyInput): Promise<any[]> {
    //   if (!this.module) {
    //     throw new Error('Strategy not loaded')
    //   }
    //   const results = []
    //   const {Strategy} = this.module
    //   const paramsMemoryRef = this.module.__pin(
    //     this.module.__newString(JSON.stringify(this.configParams)),
    //   )
    //   // const parsedCandles = this.candles.filter(candle => {
    //   //   return Number(candle.timestamp) / 1000 < data.currentTime
    //   // }).map(val => {
    //   //   return {
    //   //     open: Number(val.open).toFixed(2),
    //   //     high: Number(val.high).toFixed(2),
    //   //     close: Number(val.close).toFixed(2),
    //   //     low: Number(val.low).toFixed(2),
    //   //   }
    //   // })
    //   const jsonPrices = JSON.stringify({
    //     data: this.candles.filter(candle => {
    //       return Number(candle.timestamp) / 1000 < data.currentTime
    //     }).slice(-(Math.round(this.configParams.period * 1.5))).map(val => {
    //       return {
    //         open: Number(val.open).toFixed(6),
    //         high: Number(val.high).toFixed(6),
    //         close: Number(val.close).toFixed(6),
    //         low: Number(val.low).toFixed(6),
    //       }
    //     }),
    //   })
    //   const pricesMemoryRef = this.module.__pin(
    //     this.module.__newString(jsonPrices),
    //   )
    //   // ./bin/dev strategy backtest -p 0x99ac8ca7087fa4a2a1fb6357269965a2014abc35 ../strategy-keltner-channel/build/optimized.wasm
    //   // ./bin/dev strategy backtest ../strategy-keltner-channel/build/optimized.wasm
    //   const strategy = new Strategy(paramsMemoryRef, 120)
    //   // console.log('Input:', jsonPrices)
    //   let result
    //   try {
    //     result = await strategy.execute(pricesMemoryRef)
    //   } catch {
    //     await fs.promises.writeFile('./error.json', jsonPrices)
    //     console.error(jsonPrices)
    //   }
    //   // const unpinnedResult = this.module.__getString(result)
    //   // console.log(unpinnedResult, 'Result', (typeof unpinnedResult))
    //   const parsedResult = JSON.parse(this.module.__getString(result))
    //   // console.log('Output:', parsedResult)
    //   this.module.__unpin(pricesMemoryRef)
    //   results.push(parsedResult)
    //   this.results = results
    //   return parsedResult
    // }
    // @note
    async reloadExecute(data) {
        const importObject = {
            env: {
                abort(msg, file, line, column) {
                    console.error('abort called at ' + file + ' line:' + line + ':' + column, 'msg: ' + msg);
                },
            },
        };
        const file = fs.readFileSync(this.filePath);
        const instance = await loader_1.default.instantiate(toArrayBuffer(file), importObject);
        this.module = instance.exports;
        if (!this.module) {
            throw new Error('Strategy not loaded');
        }
        const results = [];
        // const parsedCandles = this.candles.filter(candle => {
        //   return Number(candle.timestamp) / 1000 < data.currentTime
        // }).map(val => {
        //   return {
        //     open: Number(val.open).toFixed(2),
        //     high: Number(val.high).toFixed(2),
        //     close: Number(val.close).toFixed(2),
        //     low: Number(val.low).toFixed(2),
        //   }
        // })
        const jsonPrices = JSON.stringify({
            data: [this.candles.filter(candle => {
                    return Number(candle.timestamp) / 1000 < data.currentTime;
                }).slice(-((this.numCandles))).map(val => {
                    return {
                        open: Number(val.open),
                        high: Number(val.high),
                        close: Number(val.close),
                        low: Number(val.low),
                    };
                })],
        });
        const pricesMemoryRef = this.module.__pin(this.module.__newString(jsonPrices));
        // ./bin/dev strategy backtest -p 0x99ac8ca7087fa4a2a1fb6357269965a2014abc35 ../strategy-keltner-channel/build/untouched.wasm
        // ./bin/dev strategy backtest ../strategy-keltner-channel/build/untouched.wasm
        const configParamRef = this.module.__pin(this.module.__newString(JSON.stringify(this.configParams)));
        // Check versioning
        let version;
        try {
            version = this.module.Strategy.version();
        }
        catch {
            version = 1;
        }
        let execObj; // If the bundle is class based this is our instance
        if (version === 1) {
            this.module.initialize(configParamRef);
        }
        else {
            // eslint-disable-next-line new-cap
            execObj = this.module.Strategy(configParamRef);
        }
        let result;
        try {
            const _result = version === 1 ? this.module.execute(pricesMemoryRef) : execObj.execute(pricesMemoryRef);
            result = this.module.__getString(_result);
        }
        catch {
            await fs.promises.writeFile('./error.json', jsonPrices);
            console.error(jsonPrices);
        }
        // const unpinnedResult = this.module.__getString(result)
        // console.log(unpinnedResult, 'Result', (typeof unpinnedResult))
        const parsedResult = JSON.parse((result));
        // console.log('Output:', this.module.__getString(result))
        // this.module.__unpin(pricesMemoryRef)
        results.push(parsedResult);
        this.results = results;
        return parsedResult;
    }
    async _getConfig() {
        if (!this.module) {
            throw new Error('Strategy not loaded');
        }
        // const {Strategy} = this.module
        let result;
        try {
            result = this.module.config();
        }
        catch {
            result = this.module.Strategy.config();
        }
        return JSON.parse(this.module.__getString(result));
    }
    async getConfig() {
        if (!this.config) {
            throw new Error('Strategy not loaded, load strategy before using getConfig()');
        }
        return this.config;
    }
    setConfigParams(configParams) {
        this.configParams = configParams;
        return this.configParams;
    }
    setCandles(candles) {
        this.candles = candles;
        return this.candles;
    }
    validateParams(configParams) {
        const ajv = new ajv_1.default({ allErrors: true, strictSchema: false });
        const validate = ajv.compile(this.config);
        const valid = validate(configParams);
        return [valid, validate.errors];
    }
    getResults() {
        if (!this.results) {
            throw new Error('Strategy not executed');
        }
        return this.results;
    }
}
exports.Strategy = Strategy;
