import { getAddressCodec } from '@solana/kit';
import { assert, strip0x } from '@hyperlane-xyz/utils';
const ADDRESS_CODEC = getAddressCodec();
import { PROGRAM_INSTRUCTION_DISCRIMINATOR, SYSTEM_PROGRAM_ADDRESS, } from '../constants.js';
import { ByteCursor, concatBytes, option, u8 } from '../codecs/binary.js';
import { decodeDomainedValidatorsAndThreshold, encodeDomainedValidatorsAndThreshold, } from '../codecs/shared.js';
import { isProgramInstructionDiscriminator } from './interfaces.js';
import { buildInstruction, readonlyAccount, writableAccount, writableSigner, } from './utils.js';
import { deriveMultisigIsmAccessControlPda, deriveMultisigIsmDomainDataPda, } from '../pda.js';
export var MultisigIsmMessageIdProgramInstructionKind;
(function (MultisigIsmMessageIdProgramInstructionKind) {
    MultisigIsmMessageIdProgramInstructionKind[MultisigIsmMessageIdProgramInstructionKind["Initialize"] = 0] = "Initialize";
    MultisigIsmMessageIdProgramInstructionKind[MultisigIsmMessageIdProgramInstructionKind["SetValidatorsAndThreshold"] = 1] = "SetValidatorsAndThreshold";
    MultisigIsmMessageIdProgramInstructionKind[MultisigIsmMessageIdProgramInstructionKind["GetOwner"] = 2] = "GetOwner";
    MultisigIsmMessageIdProgramInstructionKind[MultisigIsmMessageIdProgramInstructionKind["TransferOwnership"] = 3] = "TransferOwnership";
})(MultisigIsmMessageIdProgramInstructionKind || (MultisigIsmMessageIdProgramInstructionKind = {}));
export function encodeMultisigIsmMessageIdProgramInstruction(instruction) {
    switch (instruction.kind) {
        case 'initialize':
            return concatBytes(PROGRAM_INSTRUCTION_DISCRIMINATOR, u8(MultisigIsmMessageIdProgramInstructionKind.Initialize));
        case 'setValidatorsAndThreshold':
            return concatBytes(PROGRAM_INSTRUCTION_DISCRIMINATOR, u8(MultisigIsmMessageIdProgramInstructionKind.SetValidatorsAndThreshold), encodeDomainedValidatorsAndThreshold(instruction.value));
        case 'getOwner':
            return concatBytes(PROGRAM_INSTRUCTION_DISCRIMINATOR, u8(MultisigIsmMessageIdProgramInstructionKind.GetOwner));
        case 'transferOwnership':
            return concatBytes(PROGRAM_INSTRUCTION_DISCRIMINATOR, u8(MultisigIsmMessageIdProgramInstructionKind.TransferOwnership), option(instruction.newOwner, (owner) => ADDRESS_CODEC.encode(owner)));
    }
}
export function decodeMultisigIsmMessageIdProgramInstruction(data) {
    if (data.length < 9)
        return null;
    const cursor = new ByteCursor(data);
    cursor.readBytes(8);
    if (!isProgramInstructionDiscriminator(data))
        return null;
    const kind = cursor.readU8();
    switch (kind) {
        case MultisigIsmMessageIdProgramInstructionKind.Initialize:
            return { kind: 'initialize' };
        case MultisigIsmMessageIdProgramInstructionKind.SetValidatorsAndThreshold:
            return {
                kind: 'setValidatorsAndThreshold',
                value: decodeDomainedValidatorsAndThreshold(cursor),
            };
        case MultisigIsmMessageIdProgramInstructionKind.GetOwner:
            return { kind: 'getOwner' };
        case MultisigIsmMessageIdProgramInstructionKind.TransferOwnership: {
            const hasOwner = cursor.readU8() === 1;
            return {
                kind: 'transferOwnership',
                newOwner: hasOwner ? ADDRESS_CODEC.decode(cursor.readBytes(32)) : null,
            };
        }
        default:
            if (kind <= MultisigIsmMessageIdProgramInstructionKind.TransferOwnership) {
                throw new Error(`MultisigIsmMessageId instruction kind ${kind} is recognized but decoding is not yet implemented`);
            }
            return null;
    }
}
export async function getInitializeMultisigIsmMessageIdInstruction(programAddress, owner) {
    const { address: accessControl } = await deriveMultisigIsmAccessControlPda(programAddress);
    return buildInstruction(programAddress, [
        writableSigner(owner),
        writableAccount(accessControl),
        readonlyAccount(SYSTEM_PROGRAM_ADDRESS),
    ], encodeMultisigIsmMessageIdProgramInstruction({ kind: 'initialize' }));
}
export async function getSetValidatorsAndThresholdInstruction(args) {
    const { address: accessControl } = await deriveMultisigIsmAccessControlPda(args.programAddress);
    const { address: domainData } = await deriveMultisigIsmDomainDataPda(args.programAddress, args.domain);
    const validators = args.validators.map((v) => typeof v === 'string' ? hexToBytes20(v) : v);
    return buildInstruction(args.programAddress, [
        writableSigner(args.owner),
        readonlyAccount(accessControl),
        writableAccount(domainData),
        readonlyAccount(SYSTEM_PROGRAM_ADDRESS),
    ], encodeMultisigIsmMessageIdProgramInstruction({
        kind: 'setValidatorsAndThreshold',
        value: {
            domain: args.domain,
            data: {
                validators,
                threshold: args.threshold,
            },
        },
    }));
}
export async function getTransferOwnershipInstruction(programAddress, owner, newOwner) {
    const { address: accessControl } = await deriveMultisigIsmAccessControlPda(programAddress);
    return buildInstruction(programAddress, [writableSigner(owner), writableAccount(accessControl)], encodeMultisigIsmMessageIdProgramInstruction({
        kind: 'transferOwnership',
        newOwner,
    }));
}
function hexToBytes20(value) {
    const hex = strip0x(value);
    assert(/^[0-9a-fA-F]{40}$/.test(hex), `Expected 20-byte hex validator, got ${value}`);
    const out = new Uint8Array(20);
    for (let i = 0; i < 20; i += 1) {
        out[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
    }
    return out;
}
//# sourceMappingURL=multisig-ism-message-id.js.map