import { promises as fs } from 'fs';
import { Tool, ActionContext, GenericToolSchema } from '../types/tool';
import { promisify } from 'util';
import { exec } from 'child_process';

const execPromise = promisify(exec);

export async function loadToolsFromFile(filePath: string): Promise<Tool[]> {
  try {
    const fileContent = await fs.readFile(filePath, 'utf-8');
    const toolsData = JSON.parse(fileContent);
    return Object.entries(toolsData).map(([name, data]: [string, any]) => loadToolFromJson(name, data));
  } catch (error: unknown) {
    console.error(`Error loading tools from ${filePath}:`, error);
    throw new Error(`Failed to load tools from ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
  }
}

export function loadToolFromJson(name: string, toolData: any): Tool {
  if (!isValidToolData(toolData)) {
    throw new Error(`Invalid tool data for ${name}`);
  }

  const tool: Tool = {
    name,
    description: toolData.description,
    execute: createExecuteFunction(toolData.code),
    getSchema: (): GenericToolSchema => ({
      name,
      description: toolData.description,
      parameters: {
        type: 'object',
        properties: {
          ...toolData.input_schema
        }
      }
    }),
    validate: createValidateFunction(toolData.input_schema)
  };

  return tool;
}

function isValidToolData(toolData: any): boolean {
  return (
    toolData &&
    typeof toolData.description === 'string' &&
    typeof toolData.code === 'string' &&
    isValidSchema(toolData.input_schema) &&
    isValidSchema(toolData.output_schema)
  );
}

function isValidSchema(schema: any): boolean {
  return (
    schema &&
    typeof schema === 'object'
  );
}

function createExecuteFunction(code: string): (params: any, context: ActionContext) => Promise<any> {
  return async (params: any, context: ActionContext) => {
    try {
      const func = new Function('params', 'context', 'execPromise', 'require', `
        return (async () => { 
          ${code}
        })();
      `);
      return await func(params, context, execPromise, require);
    } catch (error: unknown) {
      console.error('Error executing tool:', error);
      throw new Error(`Tool execution failed: ${error instanceof Error ? error.message : String(error)}`);
    }
  };
}

function createValidateFunction(inputSchema: any): (args: Record<string, any>) => boolean {
  return (args: Record<string, any>): boolean => {
    for (const [key, value] of Object.entries(args)) {
      const paramSchema = inputSchema[key];
      if (!paramSchema) {
        console.error(`Unknown parameter: ${key}`);
        return false;
      }

      if (!validateType(value, paramSchema)) {
        console.error(`Invalid type for parameter ${key}. Expected ${paramSchema}, got ${typeof value}`);
        return false;
      }
    }

    return true;
  };
}

function validateType(value: any, expectedType: string): boolean {
  switch (expectedType) {
    case 'string':
      return typeof value === 'string';
    case 'number':
      return typeof value === 'number' && !isNaN(value);
    case 'boolean':
      return typeof value === 'boolean';
    case 'array':
      return Array.isArray(value);
    case 'object':
      return typeof value === 'object' && value !== null && !Array.isArray(value);
    default:
      console.warn(`Unknown type for validation: ${expectedType}`);
      return true;
  }
}