"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DlcClose = void 0;
const bufio_1 = require("@node-dlc/bufio");
const MessageType_1 = require("../MessageType");
const deserializeTlv_1 = require("../serialize/deserializeTlv");
const getTlv_1 = require("../serialize/getTlv");
const util_1 = require("../util");
const FundingInput_1 = require("./FundingInput");
const FundingSignatures_1 = require("./FundingSignatures");
const ScriptWitnessV0_1 = require("./ScriptWitnessV0");
/**
 * DlcClose message contains information about a node and indicates its
 * desire to close an existing contract.
 * Updated to follow DlcOffer architectural patterns.
 */
class DlcClose {
    constructor() {
        /**
         * The type for close_dlc message. close_dlc = 52170
         */
        this.type = DlcClose.type;
        // New fields as per dlcspecs PR #163
        this.protocolVersion = MessageType_1.PROTOCOL_VERSION; // Default to current protocol version
        this.fundingInputs = [];
    }
    /**
     * Creates a DlcClose from JSON data (e.g., from test vectors)
     * Handles both our internal format and external test vector formats
     * @param json JSON object representing a DLC close message
     */
    // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
    static fromJSON(json) {
        const instance = new DlcClose();
        // Basic fields with field name variations
        instance.protocolVersion =
            json.protocolVersion || json.protocol_version || MessageType_1.PROTOCOL_VERSION;
        // Basic fields with field name variations
        instance.contractId = Buffer.from(json.contractId || json.contract_id, 'hex');
        instance.closeSignature = Buffer.from(json.closeSignature || json.close_signature, 'hex');
        // Use toBigInt helper to handle BigInt values from json-bigint
        instance.offerPayoutSatoshis = (0, util_1.toBigInt)(json.offerPayoutSatoshis || json.offer_payout_satoshis);
        instance.acceptPayoutSatoshis = (0, util_1.toBigInt)(json.acceptPayoutSatoshis || json.accept_payout_satoshis);
        instance.fundInputSerialId = (0, util_1.toBigInt)(json.fundInputSerialId || json.fund_input_serial_id);
        // Use FundingInput.fromJSON() for each funding input - proper delegation
        instance.fundingInputs = (json.fundingInputs || json.funding_inputs || [])
            // eslint-disable-next-line @typescript-eslint/no-explicit-any
            .map((inputJson) => FundingInput_1.FundingInput.fromJSON(inputJson));
        // Create FundingSignatures manually since it doesn't have fromJSON
        if (json.fundingSignatures || json.funding_signatures) {
            instance.fundingSignatures = new FundingSignatures_1.FundingSignatures();
            const sigData = json.fundingSignatures || json.funding_signatures;
            // Handle different possible structures
            if (sigData.fundingSignatures) {
                // Standard format
                instance.fundingSignatures.witnessElements =
                    sigData.fundingSignatures.map(
                    // eslint-disable-next-line @typescript-eslint/no-explicit-any
                    (sig) => 
                    // eslint-disable-next-line @typescript-eslint/no-explicit-any
                    sig.witnessElements?.map((elem) => {
                        const witness = new ScriptWitnessV0_1.ScriptWitnessV0();
                        witness.length =
                            elem.length || Buffer.from(elem.witness || '', 'hex').length;
                        witness.witness = Buffer.from(elem.witness || '', 'hex');
                        return witness;
                    }) || []);
            }
            else if (Array.isArray(sigData)) {
                // Array format
                instance.fundingSignatures.witnessElements = sigData.map(
                // eslint-disable-next-line @typescript-eslint/no-explicit-any
                (sig) => 
                // eslint-disable-next-line @typescript-eslint/no-explicit-any
                sig.witnessElements?.map((elem) => {
                    const witness = new ScriptWitnessV0_1.ScriptWitnessV0();
                    witness.length =
                        elem.length || Buffer.from(elem.witness || '', 'hex').length;
                    witness.witness = Buffer.from(elem.witness || '', 'hex');
                    return witness;
                }) || []);
            }
        }
        return instance;
    }
    /**
     * Deserializes a close_dlc message with backward compatibility
     * Detects old format (without protocol_version) vs new format (with protocol_version)
     * @param buf
     */
    static deserialize(buf) {
        const instance = new DlcClose();
        const reader = new bufio_1.BufferReader(buf);
        const type = reader.readUInt16BE(); // read type
        // Validate type matches expected DlcClose type
        if (type !== MessageType_1.MessageType.DlcClose) {
            throw new Error(`Invalid message type. Expected ${MessageType_1.MessageType.DlcClose}, got ${type}`);
        }
        // Read protocol version
        instance.protocolVersion = reader.readUInt32BE();
        instance.contractId = reader.readBytes(32);
        instance.closeSignature = reader.readBytes(64);
        instance.offerPayoutSatoshis = reader.readUInt64BE();
        instance.acceptPayoutSatoshis = reader.readUInt64BE();
        instance.fundInputSerialId = reader.readUInt64BE();
        // Changed from u16 to bigsize for consistency with DlcOffer
        const fundingInputsLen = Number(reader.readBigSize());
        for (let i = 0; i < fundingInputsLen; i++) {
            // FundingInput body is serialized directly without TLV wrapper in rust-dlc format
            const fundingInput = FundingInput_1.FundingInput.deserializeBody(Buffer.from(reader.buffer.subarray(reader.position)));
            instance.fundingInputs.push(fundingInput);
            // Skip past the FundingInput we just read
            const fundingInputLength = fundingInput.serializeBody().length;
            reader.position += fundingInputLength;
        }
        // Handle FundingSignatures - deserialize raw data (no TLV wrapper) like DlcSign
        instance.fundingSignatures = FundingSignatures_1.FundingSignatures.deserialize(Buffer.from(reader.buffer.subarray(reader.position)));
        // Skip past the funding signatures we just read
        const fundingLength = instance.fundingSignatures.serialize().length;
        reader.position += fundingLength;
        // Parse any additional TLV stream (for future extensibility)
        while (!reader.eof) {
            const buf = (0, getTlv_1.getTlv)(reader);
            const tlvReader = new bufio_1.BufferReader(buf);
            const { type } = (0, deserializeTlv_1.deserializeTlv)(tlvReader);
            // Store unknown TLVs for future compatibility
            if (!instance.unknownTlvs) {
                instance.unknownTlvs = [];
            }
            instance.unknownTlvs.push({ type: Number(type), data: buf });
        }
        return instance;
    }
    /**
     * Validates correctness of all fields
     * @throws Will throw an error if validation fails
     */
    validate() {
        // Type is set automatically in class
        // protocol_version validation
        if (this.protocolVersion !== MessageType_1.PROTOCOL_VERSION) {
            throw new Error(`Unsupported protocol version: ${this.protocolVersion}, expected: ${MessageType_1.PROTOCOL_VERSION}`);
        }
        // contractId validation
        if (!this.contractId || this.contractId.length !== 32) {
            throw new Error('contractId must be 32 bytes');
        }
        // closeSignature validation
        if (!this.closeSignature || this.closeSignature.length !== 64) {
            throw new Error('closeSignature must be 64 bytes');
        }
        // Ensure input serial ids are unique
        const inputSerialIds = this.fundingInputs.map((input) => input.inputSerialId);
        if (new Set(inputSerialIds).size !== inputSerialIds.length) {
            throw new Error('inputSerialIds must be unique');
        }
        // Ensure funding inputs are segwit
        this.fundingInputs.forEach((input) => input.validate());
        // Note: FundingSignatures doesn't have a validate method, so we skip validation
    }
    /**
     * Converts dlc_close to JSON (canonical rust-dlc format)
     */
    toJSON() {
        // Include unknown TLVs for debugging
        const tlvs = [];
        if (this.unknownTlvs) {
            this.unknownTlvs.forEach((tlv) => tlvs.push({ type: tlv.type, data: tlv.data.toString('hex') }));
        }
        // Return canonical rust-dlc format
        return {
            protocolVersion: this.protocolVersion,
            contractId: this.contractId.toString('hex'),
            closeSignature: this.closeSignature.toString('hex'),
            offerPayoutSatoshis: (0, util_1.bigIntToNumber)(this.offerPayoutSatoshis),
            acceptPayoutSatoshis: (0, util_1.bigIntToNumber)(this.acceptPayoutSatoshis),
            fundInputSerialId: (0, util_1.bigIntToNumber)(this.fundInputSerialId),
            fundingInputs: this.fundingInputs.map((input) => input.toJSON()),
            fundingSignatures: this.fundingSignatures.toJSON(),
        }; // Allow different field names from interface
    }
    /**
     * Serializes the close_dlc message into a Buffer
     * Updated serialization format to match DlcOffer patterns
     */
    serialize() {
        const writer = new bufio_1.BufferWriter();
        writer.writeUInt16BE(this.type);
        // New fields as per dlcspecs PR #163
        writer.writeUInt32BE(this.protocolVersion);
        writer.writeBytes(this.contractId);
        writer.writeBytes(this.closeSignature);
        writer.writeUInt64BE(this.offerPayoutSatoshis);
        writer.writeUInt64BE(this.acceptPayoutSatoshis);
        writer.writeUInt64BE(this.fundInputSerialId);
        // Changed from u16 to bigsize for consistency with DlcOffer
        writer.writeBigSize(this.fundingInputs.length);
        for (const fundingInput of this.fundingInputs) {
            // Use serializeBody() to match rust-dlc behavior - funding inputs in vec are serialized without TLV wrapper
            writer.writeBytes(fundingInput.serializeBody());
        }
        // Serialize FundingSignatures directly (no TLV wrapper) like DlcSign
        writer.writeBytes(this.fundingSignatures.serialize());
        // Write unknown TLVs for forward compatibility
        if (this.unknownTlvs) {
            this.unknownTlvs.forEach((tlv) => {
                writer.writeBytes(tlv.data);
            });
        }
        return writer.toBuffer();
    }
}
exports.DlcClose = DlcClose;
DlcClose.type = MessageType_1.MessageType.DlcClose;
//# sourceMappingURL=DlcClose.js.map