import algosdk, { modelsv2 } from 'algosdk';
import { getAlgodClient, extractNetwork } from '../algorand-client.js';
import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
import { withCommonParams } from './commonParams.js';
// Tool schemas
export const algodToolSchemas = {
    compileTeal: {
        type: 'object',
        properties: {
            source: { type: 'string', description: 'Logic that executes when the app is called (compiled TEAL as base64)' }
        },
        required: ['source']
    },
    disassembleTeal: {
        type: 'object',
        properties: {
            bytecode: { type: 'string', description: 'TEAL bytecode to disassemble into source code' }
        },
        required: ['bytecode']
    },
    sendRawTransaction: {
        type: 'object',
        properties: {
            signedTxns: {
                type: 'array',
                items: { type: 'string', description: 'Base64-encoded signed transaction' },
                description: 'Array of signed transactions to submit to the network'
            }
        },
        required: ['signedTxns']
    },
    simulateRawTransactions: {
        type: 'object',
        properties: {
            txns: {
                type: 'array',
                items: { type: 'string', description: 'Base64-encoded transaction' },
                description: 'Array of transactions to simulate'
            }
        },
        required: ['txns']
    },
    simulateTransactions: {
        type: 'object',
        properties: {
            txnGroups: {
                type: 'array',
                items: {
                    type: 'object',
                    properties: {
                        txns: {
                            type: 'array',
                            items: { type: 'object', description: 'Transaction object to simulate' },
                            description: 'Array of transactions in this group'
                        }
                    },
                    required: ['txns'],
                    description: 'Group of transactions to simulate together'
                },
                description: 'Array of transaction groups to simulate'
            },
            allowEmptySignatures: { type: 'boolean', optional: true, description: 'Allow transactions without signatures' },
            allowMoreLogging: { type: 'boolean', optional: true, description: 'Enable additional logging during simulation' },
            allowUnnamedResources: { type: 'boolean', optional: true, description: 'Allow access to resources not listed in transaction' },
            execTraceConfig: { type: 'object', optional: true, description: 'Configuration for execution trace output' },
            extraOpcodeBudget: { type: 'integer', optional: true, description: 'Additional budget for TEAL program execution' },
            round: { type: 'integer', optional: true, description: 'Round at which to simulate the transactions' }
        },
        required: ['txnGroups']
    }
};
export class AlgodManager {
    // Tool handlers
    static async handleTool(name, args) {
        try {
            const network = extractNetwork(args);
            const algodClient = getAlgodClient(network);
            switch (name) {
                case 'compile_teal':
                    if (!args.source || typeof args.source !== 'string') {
                        throw new McpError(ErrorCode.InvalidParams, 'TEAL source code is required');
                    }
                    const result = await AlgodManager.compile(args.source, algodClient);
                    return {
                        content: [{
                                type: 'text',
                                text: JSON.stringify(result, null, 2),
                            }],
                    };
                case 'disassemble_teal':
                    if (!args.bytecode || typeof args.bytecode !== 'string') {
                        throw new McpError(ErrorCode.InvalidParams, 'TEAL bytecode is required');
                    }
                    const disassembled = await AlgodManager.disassemble(args.bytecode, algodClient);
                    return {
                        content: [{
                                type: 'text',
                                text: JSON.stringify(disassembled, null, 2),
                            }],
                    };
                case 'send_raw_transaction':
                    if (!args.signedTxns || !Array.isArray(args.signedTxns)) {
                        throw new McpError(ErrorCode.InvalidParams, 'Signed transactions array is required');
                    }
                    const txns = args.signedTxns.map(txn => {
                        if (typeof txn !== 'string') {
                            throw new McpError(ErrorCode.InvalidParams, 'Each transaction must be a base64 string');
                        }
                        return algosdk.base64ToBytes(txn);
                    });
                    const sent = await AlgodManager.sendRawTransaction(txns, algodClient);
                    return {
                        content: [{
                                type: 'text',
                                text: JSON.stringify(sent, null, 2),
                            }],
                    };
                case 'simulate_raw_transactions':
                    if (!args.txns || !Array.isArray(args.txns)) {
                        throw new McpError(ErrorCode.InvalidParams, 'Transactions array is required');
                    }
                    const rawTxns = args.txns.map(txn => {
                        if (typeof txn !== 'string') {
                            throw new McpError(ErrorCode.InvalidParams, 'Each transaction must be a base64 string');
                        }
                        return algosdk.base64ToBytes(txn);
                    });
                    const simulated = await AlgodManager.simulateRawTransactions(rawTxns, algodClient);
                    return {
                        content: [{
                                type: 'text',
                                text: JSON.stringify(simulated, null, 2),
                            }],
                    };
                case 'simulate_transactions':
                    if (!args.txnGroups || !Array.isArray(args.txnGroups)) {
                        throw new McpError(ErrorCode.InvalidParams, 'Transaction groups array is required');
                    }
                    const simulateResult = await AlgodManager.simulateTransactions(args, algodClient);
                    return {
                        content: [{
                                type: 'text',
                                text: JSON.stringify(simulateResult, null, 2),
                            }],
                    };
                default:
                    console.error(`[MCP Error] Unknown tool requested: ${name}`);
                    throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
            }
        }
        catch (error) {
            if (error instanceof McpError) {
                console.error(`[MCP Error] ${error.code}: ${error.message}`);
                throw error;
            }
            console.error('[MCP Error] Unexpected error:', error);
            throw new McpError(ErrorCode.InternalError, `Operation failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
        }
    }
    static async compile(source, algodClient) {
        try {
            if (typeof source === 'string') {
                source = source.replace(/\r\n/g, '\n');
                if (!source.endsWith('\n')) {
                    source += '\n';
                }
                source = new TextEncoder().encode(source);
            }
            const response = await algodClient.compile(source).do();
            return response;
        }
        catch (error) {
            console.error('[MCP Error] Failed to compile TEAL:', error);
            throw new McpError(ErrorCode.InternalError, `Failed to compile TEAL: ${error instanceof Error ? error.message : String(error)}`);
        }
    }
    static async disassemble(bytecode, algodClient) {
        try {
            // algosdk v3 disassemble expects Uint8Array, not base64 string
            if (typeof bytecode === 'string') {
                bytecode = algosdk.base64ToBytes(bytecode);
            }
            const response = await algodClient.disassemble(bytecode).do();
            return response;
        }
        catch (error) {
            console.error('[MCP Error] Failed to disassemble TEAL:', error);
            throw new McpError(ErrorCode.InternalError, `Failed to disassemble TEAL: ${error instanceof Error ? error.message : String(error)}`);
        }
    }
    static async sendRawTransaction(signedTxns, algodClient) {
        try {
            const response = await algodClient.sendRawTransaction(signedTxns).do();
            return response;
        }
        catch (error) {
            console.error('[MCP Error] Failed to send transaction:', error);
            throw new McpError(ErrorCode.InternalError, `Failed to send transaction: ${error instanceof Error ? error.message : String(error)}`);
        }
    }
    static async simulateRawTransactions(txns, algodClient) {
        try {
            const response = await algodClient.simulateRawTransactions(txns).do();
            return response;
        }
        catch (error) {
            console.error('[MCP Error] Failed to simulate raw transactions:', error);
            throw new McpError(ErrorCode.InternalError, `Failed to simulate raw transactions: ${error instanceof Error ? error.message : String(error)}`);
        }
    }
    static async simulateTransactions(request, algodClient) {
        try {
            const simulateRequest = new modelsv2.SimulateRequest({
                txnGroups: request.txnGroups.map(group => new modelsv2.SimulateRequestTransactionGroup({ txns: group.txns })),
                allowEmptySignatures: request.allowEmptySignatures,
                allowMoreLogging: request.allowMoreLogging,
                allowUnnamedResources: request.allowUnnamedResources,
                execTraceConfig: request.execTraceConfig,
                extraOpcodeBudget: request.extraOpcodeBudget,
                round: request.round,
            });
            const response = await algodClient.simulateTransactions(simulateRequest).do();
            return response;
        }
        catch (error) {
            console.error('[MCP Error] Failed to simulate transactions:', error);
            throw new McpError(ErrorCode.InternalError, `Failed to simulate transactions: ${error instanceof Error ? error.message : String(error)}`);
        }
    }
}
AlgodManager.algodTools = [
    {
        name: 'compile_teal',
        description: 'Compile TEAL source code',
        inputSchema: withCommonParams(algodToolSchemas.compileTeal),
    },
    {
        name: 'disassemble_teal',
        description: 'Disassemble TEAL bytecode back to source',
        inputSchema: withCommonParams(algodToolSchemas.disassembleTeal),
    },
    {
        name: 'send_raw_transaction',
        description: 'Submit signed transactions to the Algorand network',
        inputSchema: withCommonParams(algodToolSchemas.sendRawTransaction),
    },
    {
        name: 'simulate_raw_transactions',
        description: 'Simulate raw transactions',
        inputSchema: withCommonParams(algodToolSchemas.simulateRawTransactions),
    },
    {
        name: 'simulate_transactions',
        description: 'Simulate transactions with detailed configuration',
        inputSchema: withCommonParams(algodToolSchemas.simulateTransactions),
    }
];
