import { defaultMoveCoder } from './index.js';
import { AptosNetwork } from './network.js';
import { AptosContext, AptosResourcesContext, AptosTransactionContext } from './context.js';
import { ListStateStorage } from '@sentio/runtime';
import { MoveFetchConfig } from '@sentio/protos';
import { ServerError, Status } from 'nice-grpc';
import { accountTypeString, parseMoveType } from '../move/index.js';
import { ALL_ADDRESS } from '../core/index.js';
import { matchType } from '@typemove/move';
import { getHandlerName, proxyProcessor } from '../utils/metrics.js';
import { AptResource } from './data.js';
const DEFAULT_FETCH_CONFIG = {
    resourceChanges: false,
    allEvents: true,
    inputs: true,
    // for backward compatibility
    supportMultisigFunc: true
};
export const DEFAULT_RESOURCE_FETCH_CONFIG = {
    owned: true
};
export class AptosProcessorState extends ListStateStorage {
    static INSTANCE = new AptosProcessorState();
}
export class AptosTransactionProcessor {
    moduleName;
    config;
    eventHandlers = [];
    callHandlers = [];
    resourceChangeHandlers = [];
    transactionIntervalHandlers = [];
    coder;
    constructor(moduleName, options) {
        this.moduleName = moduleName;
        this.config = configure(options);
        AptosProcessorState.INSTANCE.addValue(this);
        this.coder = defaultMoveCoder(this.config.network);
        // this.loadTypes(this.coder)
        return proxyProcessor(this);
    }
    onMoveEvent(handler, filter, handlerOptions) {
        let _filters = [];
        const _fetchConfig = MoveFetchConfig.fromPartial({ ...DEFAULT_FETCH_CONFIG, ...handlerOptions });
        if (Array.isArray(filter)) {
            _filters = filter;
        }
        else {
            _filters.push(filter);
        }
        // const moduleName = this.moduleName
        const processor = this;
        this.eventHandlers.push({
            handlerName: getHandlerName(),
            handler: async function (data) {
                if (!data.rawTransaction) {
                    throw new ServerError(Status.INVALID_ARGUMENT, 'event is null');
                }
                const txn = data.transaction;
                const ctx = new AptosContext(processor.moduleName, processor.config.network, processor.config.address, BigInt(txn.version), txn, data.eventIndex, processor.config.baseLabels);
                if (!handlerOptions?.skipDecoding) {
                    const decoded = await data.decodeEvent(processor.coder);
                    await handler(decoded || data.event, ctx);
                }
                else {
                    await handler(data.event, ctx);
                }
                return ctx.stopAndGetResult();
            },
            filters: _filters,
            fetchConfig: _fetchConfig,
            partitionHandler: async (data) => {
                const p = handlerOptions?.partitionKey;
                if (!p)
                    return undefined;
                if (typeof p === 'function') {
                    const decoded = await data.decodeEvent(processor.coder);
                    return p(decoded || data.event);
                }
                return p;
            }
        });
        return this;
    }
    onEntryFunctionCall(handler, filter, handlerOptions) {
        let _filters = [];
        const _fetchConfig = MoveFetchConfig.fromPartial({ ...DEFAULT_FETCH_CONFIG, ...handlerOptions });
        if (Array.isArray(filter)) {
            _filters = filter;
        }
        else {
            _filters.push(filter);
        }
        // const address = this.config.address
        // const moduleName = this.moduleName
        const processor = this;
        this.callHandlers.push({
            handlerName: getHandlerName(),
            handler: async function (data) {
                if (!data.rawTransaction) {
                    throw new ServerError(Status.INVALID_ARGUMENT, 'call is null');
                }
                const tx = data.transaction;
                const ctx = new AptosContext(processor.moduleName, processor.config.network, processor.config.address, BigInt(tx.version), tx, 0, processor.config.baseLabels);
                if (tx) {
                    const decoded = await data.decodeCall(processor.coder);
                    await handler(decoded, ctx);
                }
                return ctx.stopAndGetResult();
            },
            filters: _filters,
            fetchConfig: _fetchConfig,
            partitionHandler: async (data) => {
                const p = handlerOptions?.partitionKey;
                if (!p)
                    return undefined;
                if (typeof p === 'function') {
                    const decoded = await data.decodeCall(processor.coder);
                    return p(decoded);
                }
                return p;
            }
        });
        return this;
    }
    onTransaction(handler, transactionFilter, handleOptions) {
        const _fetchConfig = MoveFetchConfig.fromPartial({ ...DEFAULT_FETCH_CONFIG, ...handleOptions });
        const processor = this;
        const filter = { function: '', includeFailed: transactionFilter?.includeFailed };
        if (transactionFilter?.sender) {
            filter.fromAndToAddress = {
                from: transactionFilter.sender,
                to: ''
            };
        }
        this.callHandlers.push({
            handlerName: getHandlerName(),
            handler: async function (data) {
                if (!data.rawTransaction) {
                    throw new ServerError(Status.INVALID_ARGUMENT, 'call is null');
                }
                const call = data.transaction;
                const ctx = new AptosContext(processor.moduleName, processor.config.network, processor.config.address, BigInt(call.version), call, 0, processor.config.baseLabels);
                await handler(call, ctx);
                return ctx.stopAndGetResult();
            },
            filters: [filter],
            fetchConfig: _fetchConfig,
            partitionHandler: async (data) => {
                const p = handleOptions?.partitionKey;
                if (!p)
                    return undefined;
                if (typeof p === 'function') {
                    return p(data.transaction);
                }
                return p;
            }
        });
        return this;
    }
    onEvent(handler, handlerOptions) {
        this.onMoveEvent(handler, { type: '' }, { ...handlerOptions, skipDecoding: true });
        return this;
    }
    onResourceChange(handler, typeDesc, handlerOptions) {
        let typeDescArr = [];
        if (typeof typeDesc === 'string') {
            typeDescArr = [parseMoveType(typeDesc)];
        }
        else if (Array.isArray(typeDesc)) {
            typeDescArr = typeDesc.map((t) => (typeof t === 'string' ? parseMoveType(t) : t));
        }
        else {
            typeDescArr = [typeDesc];
        }
        const hasAny = typeDescArr.some((t) => t.existAnyType());
        const processor = this;
        this.resourceChangeHandlers.push({
            handlerName: getHandlerName(),
            handler: async function (data) {
                if (!data.rawResources || !data.version) {
                    throw new ServerError(Status.INVALID_ARGUMENT, 'resource is null');
                }
                const aptResource = new AptResource(data);
                const timestamp = Number(data.timestampMicros);
                const ctx = new AptosResourcesContext(processor.config.network, processor.config.address, data.version, timestamp, processor.config.baseLabels);
                let resources = await aptResource.decodeResources(processor.coder);
                if (hasAny) {
                    resources = resources.filter((r) => {
                        const rt = parseMoveType(r.type);
                        return typeDescArr.find((t) => matchType(t, rt)) !== undefined;
                    });
                }
                if (resources.length > 0) {
                    await handler(resources, ctx);
                }
                return ctx.stopAndGetResult();
            },
            type: hasAny ? typeDescArr.map((t) => t.qname) : typeDescArr.map((t) => t.getNormalizedSignature()),
            partitionHandler: async (data) => {
                const p = handlerOptions?.partitionKey;
                if (!p)
                    return undefined;
                if (typeof p === 'function') {
                    const resources = await data.decodeResources(processor.coder);
                    return p(resources);
                }
                return p;
            }
        });
        return this;
    }
    onInterval(handler, timeInterval, versionInterval, handlerOptions) {
        const processor = this;
        this.transactionIntervalHandlers.push({
            handlerName: getHandlerName(),
            handler: async function (data) {
                if (!data.rawTransaction) {
                    throw new ServerError(Status.INVALID_ARGUMENT, 'call is null');
                }
                const transaction = JSON.parse(data.rawTransaction);
                const timestampMicros = BigInt(transaction.timestamp);
                if (timestampMicros > Number.MAX_SAFE_INTEGER) {
                    throw new ServerError(Status.INVALID_ARGUMENT, 'timestamp is too large');
                }
                const ctx = new AptosTransactionContext(processor.moduleName, processor.config.network, processor.config.address, BigInt(transaction.version), transaction, 0, processor.config.baseLabels);
                await handler(transaction, ctx);
                return ctx.stopAndGetResult();
            },
            timeIntervalInMinutes: timeInterval,
            versionInterval: versionInterval,
            fetchConfig: { ...DEFAULT_FETCH_CONFIG, ...handlerOptions },
            partitionHandler: async (data) => {
                const p = handlerOptions?.partitionKey;
                if (!p)
                    return undefined;
                if (typeof p === 'function') {
                    const transaction = JSON.parse(data.rawTransaction);
                    return p(transaction);
                }
                return p;
            }
        });
        return this;
    }
    onTimeInterval(handler, timeIntervalInMinutes = 60, backfillTimeIntervalInMinutes = 240, handlerOptions) {
        return this.onInterval(handler, {
            recentInterval: timeIntervalInMinutes,
            backfillInterval: backfillTimeIntervalInMinutes
        }, undefined, handlerOptions);
    }
    onVersionInterval(handler, versionInterval = 100000, backfillVersionInterval = 400000, handlerOptions) {
        return this.onInterval(handler, undefined, { recentInterval: versionInterval, backfillInterval: backfillVersionInterval }, handlerOptions);
    }
    getChainId() {
        return this.config.network;
    }
}
export class AptosBaseProcessor extends AptosTransactionProcessor {
}
export class AptosModulesProcessor extends AptosTransactionProcessor {
    constructor(options) {
        super(ALL_ADDRESS, options);
        return proxyProcessor(this);
    }
    static bind(options) {
        return new AptosModulesProcessor(options);
    }
    onMoveEvent(handler, filter, handlerOptions) {
        return super.onMoveEvent(handler, filter, handlerOptions);
    }
}
export class AptosGlobalProcessor {
    baseProcessor;
    constructor(options) {
        this.baseProcessor = new AptosTransactionProcessor('*', options);
        return proxyProcessor(this);
    }
    static bind(options) {
        return new AptosGlobalProcessor(options);
    }
    onTransaction(handler, transactionFilter, fetchConfig) {
        this.baseProcessor.onTransaction(handler, transactionFilter, fetchConfig);
        return this;
    }
    onTimeInterval(handler, timeIntervalInMinutes = 60, backfillTimeIntervalInMinutes = 240, handlerOptions) {
        this.baseProcessor.onTimeInterval(handler, timeIntervalInMinutes, backfillTimeIntervalInMinutes, handlerOptions);
        return this;
    }
    onVersionInterval(handler, versionInterval = 100000, backfillVersionInterval = 400000, handlerOptions) {
        this.baseProcessor.onVersionInterval(handler, versionInterval, backfillVersionInterval, handlerOptions);
        return this;
    }
}
export class AptosResourceProcessorState extends ListStateStorage {
    static INSTANCE = new AptosResourceProcessorState();
}
export class AptosResourcesProcessor {
    config;
    resourceIntervalHandlers = [];
    static bind(options) {
        return new AptosResourcesProcessor(options);
    }
    constructor(options) {
        this.config = configure(options);
        AptosResourceProcessorState.INSTANCE.addValue(this);
        return proxyProcessor(this);
    }
    getChainId() {
        return this.config.network;
    }
    onInterval(handler, timeInterval, versionInterval, type, handlerOptions, handlerName = getHandlerName()) {
        const processor = this;
        this.resourceIntervalHandlers.push({
            handlerName,
            handler: async function (data) {
                if (data.timestampMicros > Number.MAX_SAFE_INTEGER) {
                    throw new ServerError(Status.INVALID_ARGUMENT, 'timestamp is too large');
                }
                const aptResource = new AptResource(data);
                const timestamp = Number(data.timestampMicros);
                const ctx = new AptosResourcesContext(processor.config.network, processor.config.address, data.version, timestamp, processor.config.baseLabels);
                await handler(aptResource.resources, ctx);
                return ctx.stopAndGetResult();
            },
            timeIntervalInMinutes: timeInterval,
            versionInterval: versionInterval,
            type: type,
            fetchConfig: { ...DEFAULT_RESOURCE_FETCH_CONFIG, ...handlerOptions },
            partitionHandler: async (data) => {
                const p = handlerOptions?.partitionKey;
                if (!p)
                    return undefined;
                if (typeof p === 'function') {
                    const aptResource = new AptResource(data);
                    return p(aptResource.resources);
                }
                return p;
            }
        });
        return this;
    }
    onTimeInterval(handler, timeIntervalInMinutes = 60, backfillTimeIntervalInMinutes = 240, type, handlerOptions) {
        return this.onInterval(handler, {
            recentInterval: timeIntervalInMinutes,
            backfillInterval: backfillTimeIntervalInMinutes
        }, undefined, type, handlerOptions);
    }
    onVersionInterval(handler, versionInterval = 100000, backfillVersionInterval = 400000, typePrefix, handlerOptions) {
        return this.onInterval(handler, undefined, { recentInterval: versionInterval, backfillInterval: backfillVersionInterval }, typePrefix, handlerOptions);
    }
    onResourceChange(handler, typeDesc) {
        let typeDescArr = [];
        if (typeof typeDesc === 'string') {
            typeDescArr = [parseMoveType(typeDesc)];
        }
        else if (Array.isArray(typeDesc)) {
            typeDescArr = typeDesc.map((t) => (typeof t === 'string' ? parseMoveType(t) : t));
        }
        else {
            typeDescArr = [typeDesc];
        }
        const hasAny = typeDescArr.some((t) => t.existAnyType());
        const processor = this;
        this.resourceIntervalHandlers.push({
            fetchConfig: DEFAULT_RESOURCE_FETCH_CONFIG,
            handlerName: getHandlerName(),
            handler: async function (data) {
                const timestamp = Number(data.timestampMicros);
                if (!data.rawResources || !data.version) {
                    throw new ServerError(Status.INVALID_ARGUMENT, 'resource is null');
                }
                const aptResource = new AptResource(data);
                const ctx = new AptosResourcesContext(processor.config.network, processor.config.address, data.version, timestamp, processor.config.baseLabels);
                let resources = (await aptResource.decodeResources(ctx.coder));
                if (hasAny) {
                    resources = resources.filter((r) => {
                        const rt = parseMoveType(r.data.type);
                        return typeDescArr.find((t) => matchType(t, rt)) != undefined;
                    });
                }
                if (resources.length > 0) {
                    await handler(resources, ctx);
                }
                return ctx.stopAndGetResult();
            },
            type: hasAny ? typeDescArr.map((t) => t.qname) : typeDescArr.map((t) => t.getNormalizedSignature())
        });
        return this;
    }
}
function configure(options) {
    let startVersion = 0n;
    if (options.startVersion !== undefined) {
        if (typeof options.startVersion === 'number') {
            startVersion = BigInt(options.startVersion);
        }
        else {
            startVersion = options.startVersion;
        }
    }
    return {
        startVersion: startVersion,
        endVersion: options.endVersion ? BigInt(options.endVersion) : undefined,
        address: options.address === ALL_ADDRESS ? ALL_ADDRESS : accountTypeString(options.address), // aptos don't use address string in api, so only use type string
        network: options.network || AptosNetwork.MAIN_NET,
        baseLabels: options.baseLabels
    };
}
//# sourceMappingURL=aptos-processor.js.map