import { address as parseAddress } from '@solana/kit';
import { IsmType } from '@hyperlane-xyz/provider-sdk/altvm';
import { ArtifactState, } from '@hyperlane-xyz/provider-sdk/artifact';
import { assert, retryAsync } from '@hyperlane-xyz/utils';
import { resolveProgram } from '../deploy/resolve-program.js';
import { getInitializeMultisigIsmMessageIdInstruction, getSetValidatorsAndThresholdInstruction, } from '../instructions/multisig-ism-message-id.js';
import { fetchMultisigIsmAccessControl, fetchMultisigIsmDomainData, validatorBytesToHex, } from './ism-query.js';
const CHUNK_SIZE = 5;
const INIT_RETRY_ATTEMPTS = 8;
const INIT_RETRY_BASE_MS = 1000;
function toProgramDeploymentError(error) {
    if (error instanceof Error)
        return error;
    return new Error(String(error));
}
function isProgramDeploymentRace(error) {
    const logs = toProgramDeploymentError(error).context?.logs;
    return !!logs?.some((log) => log.includes('Program is not deployed') ||
        log.includes('invalid account data for instruction'));
}
export class SvmMessageIdMultisigIsmReader {
    rpc;
    constructor(rpc) {
        this.rpc = rpc;
    }
    async read(address) {
        const programId = parseAddress(address);
        const accessControl = await fetchMultisigIsmAccessControl(this.rpc, programId);
        if (accessControl === null) {
            throw new Error(`Multisig ISM not initialized at program: ${programId}`);
        }
        // TODO: The SVM multisig ISM stores validators/threshold per-domain rather
        // than globally. Proper reading will be added in a future PR.
        return {
            artifactState: ArtifactState.DEPLOYED,
            config: {
                type: IsmType.MESSAGE_ID_MULTISIG,
                validators: [],
                threshold: 0,
            },
            deployed: { address: programId, programId },
        };
    }
    async readDomain(programId, domain) {
        const domainData = await fetchMultisigIsmDomainData(this.rpc, programId, domain);
        if (domainData === null)
            return null;
        return {
            validators: validatorBytesToHex(domainData.validatorsAndThreshold.validators),
            threshold: domainData.validatorsAndThreshold.threshold,
        };
    }
}
export class SvmMessageIdMultisigIsmWriter extends SvmMessageIdMultisigIsmReader {
    svmSigner;
    constructor(rpc, svmSigner) {
        super(rpc);
        this.svmSigner = svmSigner;
    }
    async create(artifact) {
        const config = artifact.config;
        const { programAddress, receipts } = await resolveProgram(config.program, this.svmSigner, this.rpc);
        const accessControl = await fetchMultisigIsmAccessControl(this.rpc, programAddress);
        if (accessControl === null) {
            const initIx = await getInitializeMultisigIsmMessageIdInstruction(programAddress, this.svmSigner.signer);
            const initReceipt = await retryAsync(async () => {
                try {
                    return await this.svmSigner.send({ instructions: [initIx] });
                }
                catch (error) {
                    const wrapped = toProgramDeploymentError(error);
                    if (isProgramDeploymentRace(wrapped))
                        throw wrapped;
                    wrapped.isRecoverable = false;
                    throw wrapped;
                }
            }, INIT_RETRY_ATTEMPTS, INIT_RETRY_BASE_MS);
            receipts.push(initReceipt);
        }
        if (config.domains) {
            const domainInstructions = await Promise.all(Object.entries(config.domains).map(async ([domainStr, domainConfig]) => {
                const domain = Number(domainStr);
                assert(Number.isInteger(domain) && domain >= 0, `Invalid domain: '${domainStr}'`);
                return getSetValidatorsAndThresholdInstruction({
                    programAddress,
                    owner: this.svmSigner.signer,
                    domain,
                    validators: domainConfig.validators,
                    threshold: domainConfig.threshold,
                });
            }));
            for (let i = 0; i < domainInstructions.length; i += CHUNK_SIZE) {
                const chunk = domainInstructions.slice(i, i + CHUNK_SIZE);
                const tx = { instructions: chunk };
                const receipt = await this.svmSigner.send(tx);
                receipts.push(receipt);
            }
        }
        else if (config.validators.length > 0) {
            throw new Error('Single validators/threshold config not supported on Solana. Use domains map.');
        }
        return [
            {
                artifactState: ArtifactState.DEPLOYED,
                config: config,
                deployed: { address: programAddress, programId: programAddress },
            },
            receipts,
        ];
    }
    async update(artifact) {
        const programId = artifact.deployed.programId;
        return this.getUpdateDomainTxs(artifact, programId);
    }
    // TODO: The SVM multisig ISM requires per-domain diffing to compute updates.
    // Proper update logic will be added in a future PR.
    async getUpdateDomainTxs(_artifact, _programId) {
        return [];
    }
    async getUpdateDomainTx(programId, domain, validators, threshold) {
        const ix = await getSetValidatorsAndThresholdInstruction({
            programAddress: programId,
            owner: this.svmSigner.signer,
            domain,
            validators,
            threshold,
        });
        return {
            feePayer: this.svmSigner.signer.address,
            instructions: [ix],
            annotation: `Set validators for domain ${domain}`,
        };
    }
}
//# sourceMappingURL=multisig-ism.js.map