"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.OracleInfoV0 = exports.OracleInfo = exports.MultiOracleInfo = exports.SingleOracleInfo = exports.OracleParams = void 0;
const bufio_1 = require("@node-dlc/bufio");
const MessageType_1 = require("../MessageType");
const getTlv_1 = require("../serialize/getTlv");
const OracleAnnouncement_1 = require("./OracleAnnouncement");
/**
 * OracleParams describe allowed differences between oracles in
 * numerical outcome contracts, as per rust-dlc specification.
 */
class OracleParams {
    constructor() {
        this.type = OracleParams.type;
    }
    /**
     * Creates an OracleParams from JSON data
     * @param json JSON object representing oracle params
     */
    // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
    static fromJSON(json) {
        const instance = new OracleParams();
        instance.maxErrorExp = json.maxErrorExp || json.max_error_exp || 0;
        instance.minFailExp = json.minFailExp || json.min_fail_exp || 0;
        instance.maximizeCoverage =
            json.maximizeCoverage || json.maximize_coverage || false;
        return instance;
    }
    /**
     * Deserializes oracle_params_v0 message
     */
    static deserialize(buf) {
        const instance = new OracleParams();
        const reader = new bufio_1.BufferReader(buf);
        reader.readBigSize(); // read type
        instance.length = reader.readBigSize();
        instance.maxErrorExp = reader.readUInt16BE();
        instance.minFailExp = reader.readUInt16BE();
        instance.maximizeCoverage = reader.readUInt8() === 1;
        return instance;
    }
    /**
     * Deserializes oracle params body without TLV wrapper (for optional deserialization)
     */
    static deserializeBody(buf) {
        const instance = new OracleParams();
        const reader = new bufio_1.BufferReader(buf);
        // No type/length to read - just the body content
        instance.maxErrorExp = reader.readUInt16BE();
        instance.minFailExp = reader.readUInt16BE();
        instance.maximizeCoverage = reader.readUInt8() === 1;
        return instance;
    }
    validate() {
        if (this.maxErrorExp < 0) {
            throw new Error('maxErrorExp must be greater than or equal to 0');
        }
        if (this.minFailExp < 0) {
            throw new Error('minFailExp must be greater than or equal to 0');
        }
        if (this.maxErrorExp >= this.minFailExp) {
            throw new Error('maxErrorExp must be less than minFailExp');
        }
    }
    toJSON() {
        return {
            maxErrorExp: this.maxErrorExp,
            minFailExp: this.minFailExp,
            maximizeCoverage: this.maximizeCoverage,
        };
    }
    serialize() {
        const writer = new bufio_1.BufferWriter();
        writer.writeBigSize(this.type);
        const dataWriter = new bufio_1.BufferWriter();
        dataWriter.writeUInt16BE(this.maxErrorExp);
        dataWriter.writeUInt16BE(this.minFailExp);
        dataWriter.writeUInt8(this.maximizeCoverage ? 1 : 0);
        writer.writeBigSize(dataWriter.size);
        writer.writeBytes(dataWriter.toBuffer());
        return writer.toBuffer();
    }
    /**
     * Serializes the oracle params body without TLV wrapper (for optional serialization)
     */
    serializeBody() {
        const writer = new bufio_1.BufferWriter();
        writer.writeUInt16BE(this.maxErrorExp);
        writer.writeUInt16BE(this.minFailExp);
        writer.writeUInt8(this.maximizeCoverage ? 1 : 0);
        return writer.toBuffer();
    }
}
exports.OracleParams = OracleParams;
OracleParams.type = MessageType_1.MessageType.OracleParamsV0;
/**
 * SingleOracleInfo contains information about a single oracle.
 */
class SingleOracleInfo {
    constructor() {
        this.type = SingleOracleInfo.type;
    }
    /**
     * Creates a SingleOracleInfo from JSON data
     * @param json JSON object representing single oracle info
     */
    // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
    static fromJSON(json) {
        const instance = new SingleOracleInfo();
        const announcementData = json.announcement || json.oracleAnnouncement;
        if (!announcementData) {
            throw new Error('announcement or oracleAnnouncement is required for single oracle info');
        }
        // Parse announcement using proper fromJSON method
        instance.announcement = OracleAnnouncement_1.OracleAnnouncement.fromJSON(announcementData);
        return instance;
    }
    /**
     * Deserializes single oracle info
     */
    static deserialize(buf) {
        const instance = new SingleOracleInfo();
        const reader = new bufio_1.BufferReader(buf);
        // Read the type and length that serialize() writes
        reader.readBigSize(); // read type
        instance.length = reader.readBigSize(); // read length
        // Read the announcement data
        instance.announcement = OracleAnnouncement_1.OracleAnnouncement.deserialize(reader.readBytes(Number(instance.length)));
        return instance;
    }
    /**
     * Returns the closest maturity date amongst all events
     */
    getClosestMaturityDate() {
        return this.announcement.oracleEvent.eventMaturityEpoch;
    }
    validate() {
        this.announcement.validate();
    }
    toJSON() {
        // Return enum variant format for Rust compatibility
        return {
            single: {
                oracleAnnouncement: this.announcement.toJSON(),
            },
        };
    }
    serialize() {
        const writer = new bufio_1.BufferWriter();
        writer.writeBigSize(this.type);
        const dataWriter = new bufio_1.BufferWriter();
        dataWriter.writeBytes(this.announcement.serialize());
        writer.writeBigSize(dataWriter.size);
        writer.writeBytes(dataWriter.toBuffer());
        return writer.toBuffer();
    }
    /**
     * Serializes the body without TLV wrapper (for embedding in ContractInfo)
     * This matches rust-dlc behavior where OracleInfo.write() doesn't add type_id
     */
    serializeBody() {
        return this.announcement.serialize();
    }
    /**
     * Deserializes the body without TLV wrapper (for embedding in ContractInfo)
     * This matches rust-dlc behavior where OracleInfo is read without type_id
     */
    static deserializeBody(buf) {
        const instance = new SingleOracleInfo();
        // No type/length to read - just the announcement data directly
        instance.announcement = OracleAnnouncement_1.OracleAnnouncement.deserialize(buf);
        return instance;
    }
}
exports.SingleOracleInfo = SingleOracleInfo;
SingleOracleInfo.type = MessageType_1.MessageType.SingleOracleInfo;
/**
 * MultiOracleInfo contains information about multiple oracles used in multi-oracle based contracts.
 */
class MultiOracleInfo {
    constructor() {
        this.type = MultiOracleInfo.type;
        /** The set of oracle announcements. */
        this.announcements = [];
    }
    /**
     * Creates a MultiOracleInfo from JSON data
     * @param json JSON object representing multi oracle info
     */
    // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
    static fromJSON(json) {
        const instance = new MultiOracleInfo();
        instance.threshold = json.threshold || 1;
        // Parse oracle announcements using proper fromJSON method
        const announcements = json.oracleAnnouncements || json.oracle_announcements || [];
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        instance.announcements = announcements.map((announcementJson) => OracleAnnouncement_1.OracleAnnouncement.fromJSON(announcementJson));
        // Parse oracle params if present (null means explicitly absent)
        const oracleParamsData = json.oracleParams || json.oracle_params;
        if (oracleParamsData !== null && oracleParamsData !== undefined) {
            // Create OracleParams from JSON data using the fromJSON method
            instance.oracleParams = OracleParams.fromJSON(oracleParamsData);
        }
        else {
            // Explicitly null/undefined - will serialize as 00 (not present)
            instance.oracleParams = undefined;
        }
        return instance;
    }
    /**
     * Deserializes multi oracle info
     */
    static deserialize(buf) {
        const instance = new MultiOracleInfo();
        const reader = new bufio_1.BufferReader(buf);
        // Read the type and length that serialize() writes
        reader.readBigSize(); // read type
        instance.length = reader.readBigSize(); // read length
        // In rust-dlc format, MultiOracleInfo body is: threshold + announcements + optional oracle params
        instance.threshold = reader.readUInt16BE();
        const numAnnouncements = Number(reader.readBigSize()); // Changed from readUInt16BE to readBigSize to match rust-dlc vec_cb
        for (let i = 0; i < numAnnouncements; i++) {
            instance.announcements.push(OracleAnnouncement_1.OracleAnnouncement.deserialize((0, getTlv_1.getTlv)(reader)));
        }
        // Optional oracle params using Optional sub-type format
        const oracleParamsData = reader.readOptional();
        if (oracleParamsData) {
            instance.oracleParams = OracleParams.deserializeBody(oracleParamsData);
        }
        return instance;
    }
    validate() {
        if (this.threshold <= 0) {
            throw new Error('threshold must be greater than 0');
        }
        if (this.threshold > this.announcements.length) {
            throw new Error('threshold cannot be greater than number of announcements');
        }
        if (this.announcements.length === 0) {
            throw new Error('must have at least one announcement');
        }
        // Validate all announcements
        this.announcements.forEach((announcement) => announcement.validate());
        // Validate oracle params if present
        if (this.oracleParams) {
            this.oracleParams.validate();
        }
    }
    /**
     * Returns the closest maturity date amongst all events
     */
    getClosestMaturityDate() {
        return Math.min(...this.announcements.map((a) => a.oracleEvent.eventMaturityEpoch));
    }
    toJSON() {
        // Return enum variant format for Rust compatibility
        return {
            multi: {
                threshold: this.threshold,
                oracleAnnouncements: this.announcements.map((a) => a.toJSON()),
                oracleParams: this.oracleParams?.toJSON(),
            },
        };
    }
    serialize() {
        const writer = new bufio_1.BufferWriter();
        // writer.writeBigSize(this.type);
        const dataWriter = new bufio_1.BufferWriter();
        dataWriter.writeUInt16BE(this.threshold);
        dataWriter.writeBigSize(this.announcements.length); // Changed from writeUInt16BE to writeBigSize to match rust-dlc vec_cb
        for (const announcement of this.announcements) {
            dataWriter.writeBytes(announcement.serialize());
        }
        // Use Optional serialization for oracle params (body content only, not TLV wrapped)
        const oracleParamsData = this.oracleParams
            ? this.oracleParams.serializeBody()
            : null;
        dataWriter.writeOptional(oracleParamsData);
        // writer.writeBigSize(dataWriter.size);
        writer.writeBytes(dataWriter.toBuffer());
        return writer.toBuffer();
    }
    /**
     * Serializes the body without TLV wrapper (for embedding in ContractInfo)
     * This matches rust-dlc behavior where OracleInfo.write() doesn't add type_id
     */
    serializeBody() {
        const writer = new bufio_1.BufferWriter();
        writer.writeUInt16BE(this.threshold);
        writer.writeBigSize(this.announcements.length); // Changed from writeUInt16BE to writeBigSize to match rust-dlc vec_cb
        for (const announcement of this.announcements) {
            writer.writeBytes(announcement.serialize());
        }
        // Use Optional serialization for oracle params (body content only, not TLV wrapped)
        const oracleParamsData = this.oracleParams
            ? this.oracleParams.serializeBody()
            : null;
        writer.writeOptional(oracleParamsData);
        return writer.toBuffer();
    }
    /**
     * Deserializes the body without TLV wrapper (for embedding in ContractInfo)
     * This matches rust-dlc behavior where OracleInfo is read without type_id
     */
    static deserializeBody(buf) {
        const instance = new MultiOracleInfo();
        const reader = new bufio_1.BufferReader(buf);
        // No type/length to read - directly read the multi-oracle body
        instance.threshold = reader.readUInt16BE();
        const numAnnouncements = Number(reader.readBigSize()); // BigSize for announcements count
        for (let i = 0; i < numAnnouncements; i++) {
            instance.announcements.push(OracleAnnouncement_1.OracleAnnouncement.deserialize((0, getTlv_1.getTlv)(reader)));
        }
        // Optional oracle params using Optional sub-type format
        const oracleParamsData = reader.readOptional();
        if (oracleParamsData) {
            instance.oracleParams = OracleParams.deserializeBody(oracleParamsData);
        }
        return instance;
    }
}
exports.MultiOracleInfo = MultiOracleInfo;
MultiOracleInfo.type = MessageType_1.MessageType.MultiOracleInfo;
/**
 * OracleInfo contains information about the oracles to be used in
 * executing a DLC. Updated to support both single and multi-oracle
 * patterns as per rust-dlc specification.
 */
class OracleInfo {
    static deserialize(buf) {
        const reader = new bufio_1.BufferReader(buf);
        const type = Number(reader.readBigSize());
        switch (type) {
            case MessageType_1.MessageType.SingleOracleInfo:
                return SingleOracleInfo.deserialize(buf);
            case MessageType_1.MessageType.MultiOracleInfo:
                return MultiOracleInfo.deserialize(buf);
            default:
                throw new Error(`Unknown oracle info type: ${type}`);
        }
    }
    /**
     * Creates an OracleInfo from JSON data (e.g., from test vectors)
     * @param json JSON object representing oracle info
     */
    // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
    static fromJSON(json) {
        if (!json) {
            throw new Error('oracleInfo is required');
        }
        // Handle direct single oracle (legacy format)
        if (json.announcement) {
            return SingleOracleInfo.fromJSON(json);
        }
        // Handle wrapped single oracle
        else if (json.single) {
            return SingleOracleInfo.fromJSON(json.single);
        }
        // Handle multi oracle
        else if (json.multi) {
            return MultiOracleInfo.fromJSON(json.multi);
        }
        else {
            throw new Error('oracleInfo must have either announcement, single, or multi');
        }
    }
    /**
     * Returns the closest maturity date amongst all events
     */
    getClosestMaturityDate() {
        if (this instanceof SingleOracleInfo) {
            return this.announcement.oracleEvent.eventMaturityEpoch;
        }
        else if (this instanceof MultiOracleInfo) {
            return this.getClosestMaturityDate();
        }
        throw new Error('Unknown oracle info type');
    }
}
exports.OracleInfo = OracleInfo;
// For backward compatibility, keep the V0 class as alias to SingleOracleInfo
class OracleInfoV0 extends SingleOracleInfo {
    /**
     * Creates an OracleInfoV0 from JSON data (alias for SingleOracleInfo.fromJSON)
     * @param json JSON object representing oracle info
     */
    // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
    static fromJSON(json) {
        return SingleOracleInfo.fromJSON(json);
    }
}
exports.OracleInfoV0 = OracleInfoV0;
//# sourceMappingURL=OracleInfo.js.map