import { ResolutionError } from '@services/resolution/ResolutionService/errors/ResolutionError.js';
import { StringLiteralHandler } from './StringLiteralHandler.js';
import { IResolutionService } from '@services/resolution/ResolutionService/IResolutionService.js';
import { ResolutionContext } from '@services/resolution/ResolutionService/IResolutionService.js';
import type { IParserService } from '@services/pipeline/ParserService/IParserService.js';
import type { MeldNode, TextNode } from 'meld-spec';

/**
 * Handles string concatenation operations using the ++ operator
 */
export class StringConcatenationHandler {
  private stringLiteralHandler: StringLiteralHandler;

  constructor(
    private resolutionService: IResolutionService,
    private parserService?: IParserService
  ) {
    this.stringLiteralHandler = new StringLiteralHandler();
  }

  /**
   * Splits a value into its concatenation parts
   * @returns Array of parts to be concatenated
   * @throws ResolutionError if the concatenation syntax is invalid
   */
  private async splitConcatenationParts(value: string): Promise<string[]> {
    // If ParserService is available, try to use it for more accurate parsing
    if (this.parserService) {
      try {
        // Create a simple element to parse with the concatenation
        // Add some context to make it valid Meld syntax
        const wrappedValue = `@text test = ${value}`;
        
        // Parse with AST
        const nodes = await this.parserService.parse(wrappedValue);
        
        // Look for directive nodes with concatenation operators
        const directiveNode = nodes.find(node => 
          node.type === 'Directive' && 
          (node as any).directive?.kind === 'text'
        );
        
        if (directiveNode) {
          // Access the value which should contain our concatenation
          const directiveValue = (directiveNode as any).directive?.value;
          
          // If the parser properly recognized the concatenation
          if (directiveValue && 
              typeof directiveValue === 'object' && 
              directiveValue.type === 'Concatenation') {
            // Return the parts directly from the AST
            return directiveValue.parts.map((p: any) => 
              typeof p === 'object' && p.raw ? p.raw : String(p)
            );
          }
        }
        
        // If AST parsing didn't identify concatenation structure,
        // fall back to simpler splitting
      } catch (error) {
        console.warn('Failed to parse concatenation with AST, falling back to manual parsing:', error);
      }
    }
    
    // Fallback: Split by ++ operator, preserving spaces around it
    const parts = value.split(/\s*\+\+\s*/);
    
    // Validate each part is non-empty
    if (parts.some(part => part.trim().length === 0)) {
      throw new ResolutionError(
        'Empty part in string concatenation',
        { value }
      );
    }

    return parts;
  }

  /**
   * Checks if a value contains the ++ operator
   */
  async hasConcatenation(value: string): Promise<boolean> {
    // Try to use the parser to detect concatenation if available
    if (this.parserService) {
      try {
        // Wrap the value for parsing
        const wrappedValue = `@text test = ${value}`;
        
        // Parse the wrapped value
        const nodes = await this.parserService.parse(wrappedValue);
        
        // Look for directive nodes with concatenation operators
        const directiveNode = nodes.find(node => 
          node.type === 'Directive' && 
          (node as any).directive?.kind === 'text'
        );
        
        if (directiveNode) {
          // Check if the parser recognized a Concatenation node
          const directiveValue = (directiveNode as any).directive?.value;
          return directiveValue && 
                 typeof directiveValue === 'object' && 
                 directiveValue.type === 'Concatenation';
        }
      } catch (error) {
        // If parsing fails, fall back to regex check
        console.warn('Failed to check concatenation with AST, falling back to regex:', error);
      }
    }
    
    // Fallback: Look for ++ with required spaces on both sides
    return /\s\+\+\s/.test(value);
  }

  /**
   * Resolves a string concatenation expression
   * @throws ResolutionError if the concatenation is invalid
   */
  async resolveConcatenation(value: string, context: ResolutionContext): Promise<string> {
    // Split into parts
    const parts = await this.splitConcatenationParts(value);

    // Resolve each part
    const resolvedParts: string[] = [];
    for (const part of parts) {
      const trimmedPart = part.trim();

      // Handle string literals
      if (this.stringLiteralHandler.isStringLiteral(trimmedPart)) {
        resolvedParts.push(this.stringLiteralHandler.parseLiteral(trimmedPart));
        continue;
      }

      // Handle variables and other expressions
      try {
        const resolved = await this.resolutionService.resolveInContext(trimmedPart, context);
        resolvedParts.push(resolved);
      } catch (error) {
        throw new ResolutionError(
          `Failed to resolve part in concatenation: ${trimmedPart}`,
          { value: trimmedPart, context, cause: error }
        );
      }
    }

    // Join all parts
    return resolvedParts.join('');
  }
} 