/**
 * Test script to verify that InscribeHashinalTool generates a form when attributes field is missing
 * 
 * This script tests the end-to-end form generation functionality for the InscribeHashinalTool
 * when required fields are missing from the input.
 * 
 * Expected behavior:
 * 1. Tool should be wrapped with FormValidatingToolWrapper (due to extendZodSchema)
 * 2. When attributes field is missing, validation should fail
 * 3. A form should be generated containing the missing attributes field
 * 4. Tool should NOT execute directly but return form generation response
 * 
 * Run with: pnpm tsx src/scripts/test-inscribe-form-generation.ts
 */

import dotenv from 'dotenv';
import { ConversationalAgent } from '../conversational-agent';
import type { ChatResponse } from '../base-agent';
import type { FormMessage } from '../forms/types';
import { NetworkType } from '@hashgraphonline/standards-sdk';

dotenv.config();

interface TestResult {
  success: boolean;
  message: string;
  details?: Record<string, unknown>;
}

interface FormGenerationTestResult {
  isFormGenerated: boolean;
  hasAttributesField: boolean;
  formMessage?: FormMessage;
  originalResponse?: ChatResponse;
}

/**
 * Validates that required environment variables are present
 */
function validateEnvironment(): TestResult {
  const required = [
    'HEDERA_OPERATOR_ID',
    'HEDERA_OPERATOR_KEY',
    'OPENAI_API_KEY'
  ];

  const missing = required.filter(key => !process.env[key]);
  
  if (missing.length > 0) {
    return {
      success: false,
      message: `Missing required environment variables: ${missing.join(', ')}`
    };
  }

  return {
    success: true,
    message: 'Environment validation passed'
  };
}

/**
 * Creates and initializes a conversational agent for testing
 */
async function createTestAgent(): Promise<ConversationalAgent> {
  const options = {
    accountId: process.env.HEDERA_OPERATOR_ID!,
    privateKey: process.env.HEDERA_OPERATOR_KEY!,
    network: (process.env.HEDERA_NETWORK as 'testnet' | 'mainnet') || 'testnet',
    openAIApiKey: process.env.OPENAI_API_KEY!,
    openAIModelName: 'gpt-4o-mini',
    verbose: true,
    disableLogging: false,
    entityMemoryEnabled: false,
  };

  const agent = new ConversationalAgent(options);
  await agent.initialize();
  
  return agent;
}

/**
 * Test the FormValidatingToolWrapper directly by accessing the underlying tools
 */
async function testDirectToolAccess(agent: ConversationalAgent): Promise<FormGenerationTestResult> {
  console.log('🔧 Testing direct tool access...');
  
  try {
    const underlyingAgent = agent.getAgent();
    const tools = (underlyingAgent as unknown as { tools: Array<{ name: string; constructor: { name: string }; _call: (input: unknown) => Promise<string> }> }).tools;
    
    console.log('Available tools:', tools.map(t => t.name));
    
    const inscribeHashinalTool = tools.find(t => t.name === 'inscribeHashinal');
    
    if (!inscribeHashinalTool) {
      console.log('❌ InscribeHashinal tool not found in tools array');
      return {
        isFormGenerated: false,
        hasAttributesField: false
      };
    }
    
    console.log('✅ Found InscribeHashinal tool:', {
      name: inscribeHashinalTool.name,
      type: inscribeHashinalTool.constructor.name,
      isFormValidatingWrapper: inscribeHashinalTool.constructor.name === 'FormValidatingToolWrapper'
    });
    
    const incompleteInput = {
      url: "https://example.com/test-image.png",
      name: "Test NFT",
      description: "A test NFT for form testing"
    };
    
    console.log('🧪 Calling tool directly with incomplete input...');
    const result = await inscribeHashinalTool._call(incompleteInput);
    
    console.log('📋 Direct tool call result:', {
      resultType: typeof result,
      resultLength: result?.length || 0,
      resultPreview: result?.substring(0, 200) + '...'
    });
    
    try {
      const parsed = JSON.parse(result);
      if (parsed.requiresForm && parsed.formMessage) {
        console.log('🎉 Form generated by direct tool call!');
        const formMessage = parsed.formMessage;
        
        const formConfig = (formMessage as { formConfig?: { fields?: Array<{ name: string; type: string }> } }).formConfig;
        const attributesField = formConfig?.fields?.find(
          field => field.name === 'attributes'
        );
        
        const hasAttributesField = !!attributesField;
        
        console.log('📝 Form analysis (direct call):', {
          formId: (formMessage as { id?: string }).id,
          toolName: (formMessage as { toolName?: string }).toolName,
          fieldCount: formConfig?.fields?.length || 0,
          hasAttributesField,
          attributesFieldType: attributesField?.type,
          fieldsPresent: formConfig?.fields?.map(f => f.name) || []
        });

        return {
          isFormGenerated: true,
          hasAttributesField,
          formMessage,
        };
      }
    } catch {
      console.log('Result is not JSON or does not contain form data');
    }
    
    return {
      isFormGenerated: false,
      hasAttributesField: false
    };
    
  } catch (error) {
    console.error('❌ Error during direct tool test:', error);
    throw error;
  }
}

/**
 * Tests that the InscribeHashinalTool is properly wrapped and generates forms for missing fields
 */
async function testInscribeFormGeneration(agent: ConversationalAgent): Promise<FormGenerationTestResult> {
  console.log('🧪 Testing form generation for InscribeHashinalTool...');

  const testMessage = `
    I need to inscribe a hashinal NFT. Please call the inscribeHashinal tool with this data:
    {
      "url": "https://example.com/test-image.png",
      "name": "Test NFT", 
      "description": "A test NFT for form testing"
    }
    
    Do not ask any questions, just call the tool now with those parameters.
  `;
  
  try {
    console.log('📤 Sending test message to agent...');
    const response = await agent.processMessage(testMessage);
    
    console.log('📋 Response received:', {
      requiresForm: response.requiresForm,
      hasFormMessage: !!response.formMessage,
      outputLength: response.output?.length || 0,
      toolCallCount: response.tool_calls?.length || 0,
      hasError: !!response.error
    });

    if (response.tool_calls && response.tool_calls.length > 0) {
      console.log('🔧 Tool calls made:', 
        response.tool_calls.map(call => ({
          name: call.name,
          hasArgs: !!call.args,
          argKeys: call.args ? Object.keys(call.args) : [],
          output: call.output?.substring(0, 100) + '...'
        }))
      );
    }

    if (response.tool_calls && response.tool_calls.length > 0) {
      for (const toolCall of response.tool_calls) {
        if (toolCall.name === 'inscribeHashinal' && toolCall.output) {
          try {
            const parsed = JSON.parse(toolCall.output);
            if (parsed.requiresForm && parsed.formMessage) {
              console.log('📝 Found form in tool call output!');
              const formMessage = parsed.formMessage;
              
              const formConfig = (formMessage as { formConfig?: { fields?: Array<{ name: string; type: string }> } }).formConfig;
              const attributesField = formConfig?.fields?.find(
                field => field.name === 'attributes'
              );
              
              const hasAttributesField = !!attributesField;
              
              console.log('📝 Form analysis:', {
                formId: (formMessage as { id?: string }).id,
                toolName: (formMessage as { toolName?: string }).toolName,
                fieldCount: formConfig?.fields?.length || 0,
                hasAttributesField,
                attributesFieldType: attributesField?.type,
                fieldsPresent: formConfig?.fields?.map(f => f.name) || []
              });

              return {
                isFormGenerated: true,
                hasAttributesField,
                formMessage,
                originalResponse: response
              };
            }
          } catch (parseError) {
            console.log('Could not parse tool output as JSON:', parseError);
          }
        }
      }
    }

    if (response.requiresForm && response.formMessage) {
      const formMessage = response.formMessage;
      
      const formConfig = (formMessage as { formConfig?: { fields?: Array<{ name: string; type: string }> } }).formConfig;
      const attributesField = formConfig?.fields?.find(
        field => field.name === 'attributes'
      );
      
      const hasAttributesField = !!attributesField;
      
      console.log('📝 Form analysis (top-level):', {
        formId: (formMessage as { id?: string }).id,
        toolName: (formMessage as { toolName?: string }).toolName,
        fieldCount: formConfig?.fields?.length || 0,
        hasAttributesField,
        attributesFieldType: attributesField?.type,
        fieldsPresent: formConfig?.fields?.map(f => f.name) || []
      });

      return {
        isFormGenerated: true,
        hasAttributesField,
        formMessage: formMessage as FormMessage,
        originalResponse: response
      };
    } else {
      console.log('⚠️  No form was generated in response');
      console.log('Response details:', {
        output: response.output?.substring(0, 200) + '...',
        toolCalls: response.tool_calls?.length || 0,
        error: response.error
      });

      return {
        isFormGenerated: false,
        hasAttributesField: false,
        originalResponse: response
      };
    }
  } catch (error) {
    console.error('❌ Error during form generation test:', error);
    throw error;
  }
}

/**
 * Verifies that the tool wrapper is working correctly by checking response structure
 */
function analyzeFormGenerationResponse(result: FormGenerationTestResult): TestResult {
  if (!result.isFormGenerated) {
    return {
      success: false,
      message: 'Form was not generated when attributes field was missing',
      details: {
        responseHadForm: result.isFormGenerated,
        originalOutput: result.originalResponse?.output?.substring(0, 200)
      }
    };
  }

  if (!result.hasAttributesField) {
    return {
      success: false,
      message: 'Form was generated but does not contain attributes field',
      details: {
        formFields: result.formMessage?.formConfig.fields.map(f => f.name) || [],
        formId: result.formMessage?.id
      }
    };
  }

  if (result.formMessage?.toolName !== 'inscribeHashinal') {
    return {
      success: false,
      message: 'Form was generated for wrong tool',
      details: {
        expectedTool: 'inscribeHashinal',
        actualTool: result.formMessage?.toolName
      }
    };
  }

  return {
    success: true,
    message: 'Form generation test passed successfully',
    details: {
      formGenerated: true,
      hasAttributesField: true,
      toolName: result.formMessage?.toolName,
      fieldCount: result.formMessage?.formConfig.fields.length,
      formId: result.formMessage?.id
    }
  };
}

/**
 * Validates that the form contains expected structure and required elements
 */
function validateFormStructure(formMessage: FormMessage): TestResult {
  const issues: string[] = [];

  if (!formMessage.id) {
    issues.push('Form missing unique ID');
  }

  if (!formMessage.formConfig) {
    issues.push('Form missing configuration');
  }

  if (!formMessage.formConfig.fields || formMessage.formConfig.fields.length === 0) {
    issues.push('Form has no fields');
  }

  if (!formMessage.formConfig.title) {
    issues.push('Form missing title');
  }

  const fieldNames = formMessage.formConfig.fields.map(f => f.name);
  const essentialFields = ['attributes'];
  const missingEssential = essentialFields.filter(field => !fieldNames.includes(field));
  
  if (missingEssential.length > 0) {
    issues.push(`Form missing essential fields: ${missingEssential.join(', ')}`);
  }

  const attributesField = formMessage.formConfig.fields.find(f => f.name === 'attributes');
  if (attributesField) {
    if (attributesField.type !== 'array') {
      issues.push(`Attributes field has wrong type: ${attributesField.type}, expected: array`);
    }
    
    if (!attributesField.required) {
      issues.push('Attributes field should be marked as required');
    }
  }

  if (issues.length > 0) {
    return {
      success: false,
      message: `Form structure validation failed: ${issues.join(', ')}`,
      details: {
        issues,
        formStructure: {
          id: formMessage.id,
          title: formMessage.formConfig.title,
          fieldCount: formMessage.formConfig.fields.length,
          fields: formMessage.formConfig.fields.map(f => ({
            name: f.name,
            type: f.type,
            required: f.required
          }))
        }
      }
    };
  }

  return {
    success: true,
    message: 'Form structure validation passed',
    details: {
      formId: formMessage.id,
      title: formMessage.formConfig.title,
      fieldCount: formMessage.formConfig.fields.length,
      attributesFieldFound: true
    }
  };
}

/**
 * Main test execution function
 */
async function runTest(): Promise<void> {
  console.log('🚀 Starting InscribeHashinalTool form generation test\n');

  try {

    console.log('📋 Step 1: Validating environment...');
    const envResult = validateEnvironment();
    if (!envResult.success) {
      console.error('❌', envResult.message);
      process.exit(1);
    }
    console.log('✅', envResult.message, '\n');

    console.log('📋 Step 2: Initializing conversational agent...');
    const agent = await createTestAgent();
    console.log('✅ Agent initialized successfully\n');

    console.log('📋 Step 3a: Testing direct tool access...');
    const directResult = await testDirectToolAccess(agent);
    
    console.log('📋 Step 3b: Testing InscribeHashinalTool form generation via conversation...');
    const formResult = directResult.isFormGenerated ? directResult : await testInscribeFormGeneration(agent);
    
    console.log('📋 Step 4: Analyzing form generation results...');
    const analysisResult = analyzeFormGenerationResponse(formResult);
    
    if (!analysisResult.success) {
      console.error('❌', analysisResult.message);
      if (analysisResult.details) {
        console.log('Details:', JSON.stringify(analysisResult.details, null, 2));
      }
      process.exit(1);
    }
    console.log('✅', analysisResult.message);
    
    if (formResult.formMessage) {
      console.log('📋 Step 5: Validating form structure...');
      const structureResult = validateFormStructure(formResult.formMessage);
      
      if (!structureResult.success) {
        console.error('❌', structureResult.message);
        if (structureResult.details) {
          console.log('Details:', JSON.stringify(structureResult.details, null, 2));
        }
        process.exit(1);
      }
      console.log('✅', structureResult.message);
    }

    console.log('\n🎉 All tests passed successfully!');
    console.log('\n📊 Test Summary:');
    console.log('✅ InscribeHashinalTool is properly wrapped with FormValidatingToolWrapper');
    console.log('✅ Form is generated when attributes field is missing');
    console.log('✅ Generated form contains attributes field for user input');
    console.log('✅ Form structure is valid and complete');
    
    if (analysisResult.details) {
      console.log('\n📋 Final Details:');
      console.log(`   Form ID: ${analysisResult.details.formId}`);
      console.log(`   Tool Name: ${analysisResult.details.toolName}`);
      console.log(`   Field Count: ${analysisResult.details.fieldCount}`);
      console.log(`   Has Attributes Field: ${analysisResult.details.hasAttributesField}`);
    }

    await agent.cleanup();
    
  } catch (error) {
    console.error('❌ Test failed with error:', error);
    console.error('Error details:', error instanceof Error ? error.stack : String(error));
    process.exit(1);
  }
}

runTest().catch((error) => {
  console.error('Unhandled test error:', error);
  process.exit(1);
});