// @lifecycle canonical - Formats prompt execution responses.
/**
 * Response Formatter - Handles response formatting and coordination
 *
 * Extracted from PromptExecutor to provide focused
 * response formatting capabilities with clear separation of concerns.
 */

import { FormatterExecutionContext, SimpleResponseFormatter } from '../core/types.js';

import type {
  Logger,
  GateValidationInfo,
  ToolResponse,
  ResponseFormatterPort,
} from '../../../../shared/types/index.js';

const fallbackLogger: Logger = {
  info: () => {},
  debug: () => {},
  warn: (...args: unknown[]) => console.warn('[ResponseFormatter]', ...args),
  error: (...args: unknown[]) => console.error('[ResponseFormatter]', ...args),
};

/**
 * Analytics service interface for execution tracking
 */
interface AnalyticsService {
  trackExecution?(data: {
    executionId: string;
    executionType: string;
    duration: number;
    frameworkUsed?: string;
    stepsExecuted?: number;
    success: boolean;
    sessionId?: string;
  }): void;
  trackError?(data: {
    executionId: string;
    executionType: string;
    errorType: string;
    errorMessage: string;
    sessionId?: string;
  }): void;
}

/**
 * Gate validation result interface (legacy, for backward compatibility)
 * For full structured response contract, use GateValidationInfo from types.
 */
interface LegacyGateValidationResult {
  passed: boolean;
  gateResults?: Array<{ passed: boolean; name?: string; message?: string }>;
  executionTime?: number;
  retryRequired?: boolean;
}

/**
 * Combined gate validation result type.
 * Accepts either legacy format or full GateValidationInfo for structured response contract.
 */
type GateValidationResult = LegacyGateValidationResult | Partial<GateValidationInfo>;

/**
 * Response format options
 */
interface ResponseFormatOptions {
  includeStructuredContent?: boolean;
  includeMetadata?: boolean;
  metadata?: Record<string, unknown>;
}

/**
 * ResponseFormatter handles all response-related formatting and coordination
 *
 * This class provides:
 * - Response formatting for different execution types
 * - Error response handling and formatting
 * - Analytics integration and tracking
 * - Structured response building
 */
function normalizeToolResponse(payload: unknown, defaultIsError = false): ToolResponse {
  if (payload && typeof payload === 'object') {
    const obj = payload as Record<string, unknown>;
    if (Array.isArray(obj['content'])) {
      const normalized: ToolResponse = {
        content: obj['content'] as ToolResponse['content'],
        isError: (obj['isError'] as boolean | undefined) ?? defaultIsError,
      };
      if (obj['structuredContent'] !== undefined) {
        normalized.structuredContent = obj['structuredContent'] as Record<string, unknown>;
      }
      if (obj['metadata'] !== undefined) {
        (normalized as unknown as Record<string, unknown>)['metadata'] = obj['metadata'];
      }
      return normalized;
    }

    if (typeof obj['content'] === 'string') {
      const structuredContent = obj['structuredContent'] as Record<string, unknown> | undefined;
      return {
        content: [{ type: 'text' as const, text: obj['content'] }],
        isError: (obj['isError'] as boolean | undefined) ?? defaultIsError,
        ...(structuredContent !== undefined && { structuredContent }),
      };
    }

    if (typeof obj['text'] === 'string') {
      const structuredContent = obj['structuredContent'] as Record<string, unknown> | undefined;
      return {
        content: [{ type: 'text' as const, text: obj['text'] }],
        isError: (obj['isError'] as boolean | undefined) ?? defaultIsError,
        ...(structuredContent !== undefined && { structuredContent }),
      };
    }

    if (typeof obj['message'] === 'string') {
      const structuredContent = obj['structuredContent'] as Record<string, unknown> | undefined;
      return {
        content: [{ type: 'text' as const, text: obj['message'] }],
        isError: (obj['isError'] as boolean | undefined) ?? defaultIsError,
        ...(structuredContent !== undefined && { structuredContent }),
      };
    }
  }

  const text = typeof payload === 'string' ? payload : JSON.stringify(payload, null, 2);

  return {
    content: [{ type: 'text' as const, text }],
    isError: defaultIsError,
  };
}

export class ResponseFormatter implements SimpleResponseFormatter, ResponseFormatterPort {
  private analyticsService?: AnalyticsService;
  private readonly logger: Logger;

  constructor(logger: Logger = fallbackLogger) {
    this.logger = logger;
  }

  /**
   * Set analytics service for tracking
   */
  public setAnalyticsService(service: AnalyticsService): void {
    this.analyticsService = service;
    this.logger.debug('📊 [ResponseFormatter] Analytics service set');
  }

  /**
   * Format general response content
   */
  public formatResponse(content: unknown): ToolResponse {
    try {
      this.logger.debug('🔧 [ResponseFormatter] Formatting general response');
      return normalizeToolResponse(content, false);
    } catch (error) {
      this.logger.error('❌ [ResponseFormatter] General response formatting failed', {
        error: error instanceof Error ? error.message : String(error),
      });
      return normalizeToolResponse(content, false);
    }
  }

  /**
   * Format prompt engine response with execution context
   */
  public formatPromptEngineResponse(
    response: unknown,
    executionContext: FormatterExecutionContext = {
      executionId: `exec_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`,
      executionType: 'single',
      startTime: Date.now(),
      endTime: Date.now(),
      frameworkEnabled: false,
      success: true,
    },
    options: ResponseFormatOptions = {},
    gateResults?: GateValidationResult
  ): ToolResponse {
    // IMPORTANT: keep the rendered template text inside `content`. Structured metadata is
    // optional and should never replace the main instructions, otherwise the LLM will only
    // see bookkeeping data. Only add structured blocks when a chain execution explicitly
    // needs them.
    try {
      this.logger.debug('🎯 [ResponseFormatter] Formatting prompt engine response', {
        executionType: executionContext?.executionType,
        frameworkUsed: executionContext?.frameworkUsed,
        stepsExecuted: executionContext?.stepsExecuted,
      });

      // Track analytics if service is available
      if (this.analyticsService && executionContext) {
        this.trackExecution(executionContext);
      }

      if (
        response &&
        typeof response === 'object' &&
        Array.isArray((response as ToolResponse).content)
      ) {
        return response as ToolResponse;
      }

      const toolResponse = normalizeToolResponse(response, false);

      // Only include structuredContent for chains (step tracking metadata)
      // Prompts and templates should return clean content without metadata clutter
      // Default to omitting structuredContent so model input stays lean unless explicitly requested.
      const includeStructuredContent =
        executionContext.executionType === 'chain' &&
        options?.['includeStructuredContent'] === true;

      if (includeStructuredContent) {
        const executionDuration = executionContext.endTime - executionContext.startTime;
        const structuredContent: Record<string, unknown> = {
          execution: {
            id: executionContext.executionId,
            type: executionContext.executionType,
            duration_ms: executionDuration,
            framework: executionContext.frameworkUsed ?? null,
            steps: executionContext.stepsExecuted ?? null,
          },
        };

        if (executionContext.chainId) {
          structuredContent['chain'] = {
            id: executionContext.chainId,
            status: executionContext.chainProgress?.status ?? 'in_progress',
            current_step: executionContext.chainProgress?.currentStep ?? null,
            total_steps: executionContext.chainProgress?.totalSteps ?? null,
          };
        }

        if (gateResults !== undefined) {
          // Check for full GateValidationInfo (structured response contract)
          const hasFullInfo = 'pendingGateIds' in gateResults;

          if (hasFullInfo) {
            // Use structured response contract format
            const fullInfo = gateResults;
            const gateValidation: Partial<GateValidationInfo> = {
              enabled: fullInfo.enabled ?? true,
              passed: fullInfo.passed ?? false,
              totalGates: fullInfo.totalGates ?? 0,
              failedGates: fullInfo.failedGates ?? [],
              executionTime: fullInfo.executionTime ?? 0,
              pendingGateIds: fullInfo.pendingGateIds ?? [],
              requiresGateVerdict: fullInfo.requiresGateVerdict ?? false,
              responseBlocked: fullInfo.responseBlocked ?? false,
              gateRetryInfo: fullInfo.gateRetryInfo ?? {
                maxAttempts: 2,
                currentAttempt: 0,
                retryAllowed: true,
              },
            };
            structuredContent['gateValidation'] = gateValidation;
          } else {
            // Legacy format
            const legacyResults = gateResults as LegacyGateValidationResult;
            const gateResultsArray = legacyResults.gateResults ?? [];
            structuredContent['gates'] = {
              enabled: true,
              passed: legacyResults.passed,
              total: gateResultsArray.length,
              failed: gateResultsArray.filter(
                (g: { passed: boolean; name?: string; message?: string }) => !g.passed
              ),
              execution_ms: legacyResults.executionTime ?? 0,
              retries: legacyResults.retryRequired === true ? 1 : 0,
            };
          }
        }

        toolResponse.structuredContent = structuredContent;
      }

      if (options?.['includeMetadata'] === true || options?.['metadata'] !== undefined) {
        const defaultMetadata: Record<string, unknown> = {
          executionType: executionContext.executionType,
          frameworkUsed: executionContext.frameworkUsed,
        };
        if (executionContext.chainId !== undefined && executionContext.chainId !== '') {
          defaultMetadata['chainId'] = executionContext.chainId;
        }
        if (executionContext.chainProgress !== undefined) {
          if (typeof executionContext.chainProgress.currentStep === 'number') {
            defaultMetadata['currentStep'] = executionContext.chainProgress.currentStep;
          }
          if (typeof executionContext.chainProgress.totalSteps === 'number') {
            defaultMetadata['totalSteps'] = executionContext.chainProgress.totalSteps;
          }
        }
        (toolResponse as unknown as Record<string, unknown>)['metadata'] = {
          ...defaultMetadata,
          ...(options?.['metadata'] ?? {}),
        };
      }

      this.logger.debug('✅ [ResponseFormatter] Prompt engine response formatted successfully');
      return toolResponse;
    } catch (error) {
      this.logger.error('❌ [ResponseFormatter] Prompt engine response formatting failed', {
        error: error instanceof Error ? error.message : String(error),
      });

      // Return error response
      return this.formatErrorResponse(error, executionContext, options);
    }
  }

  /**
   * Format error response
   */
  public formatErrorResponse(
    error: unknown,
    executionContext?: FormatterExecutionContext,
    _options?: ResponseFormatOptions
  ): ToolResponse {
    try {
      this.logger.debug('🚨 [ResponseFormatter] Formatting error response', {
        errorType: error instanceof Error ? error.constructor.name : typeof error,
      });

      // Track error analytics if service is available
      if (this.analyticsService && executionContext) {
        this.trackError(error, executionContext);
      }

      const errorMessage = error instanceof Error ? error.message : String(error);

      const toolResponse = normalizeToolResponse(`Error: ${errorMessage}`, true);

      if (executionContext?.executionType === 'chain') {
        const executionDuration = executionContext.endTime - executionContext.startTime;
        toolResponse.structuredContent = {
          execution: {
            id: executionContext.executionId,
            type: executionContext.executionType,
            duration_ms: executionDuration,
            framework: executionContext.frameworkUsed ?? null,
            steps: executionContext.stepsExecuted ?? null,
          },
          chain: executionContext.chainId
            ? {
                id: executionContext.chainId,
                status: executionContext.chainProgress?.status ?? 'in_progress',
                current_step: executionContext.chainProgress?.currentStep ?? null,
                total_steps: executionContext.chainProgress?.totalSteps ?? null,
              }
            : undefined,
        };
      }

      this.logger.debug('✅ [ResponseFormatter] Error response formatted successfully');
      return toolResponse;
    } catch (formattingError) {
      this.logger.error('❌ [ResponseFormatter] Error response formatting failed', {
        originalError: error instanceof Error ? error.message : String(error),
        formattingError:
          formattingError instanceof Error ? formattingError.message : String(formattingError),
      });

      // Fallback to minimal response
      return normalizeToolResponse(
        `Error: ${error instanceof Error ? error.message : String(error)}`,
        true
      );
    }
  }

  /**
   * Format chain execution response
   */
  public formatChainResponse(
    response: unknown,
    chainId: string,
    currentStep: number,
    totalSteps: number,
    executionContext?: FormatterExecutionContext
  ): ToolResponse {
    try {
      this.logger.debug('🔗 [ResponseFormatter] Formatting chain response', {
        chainId,
        currentStep,
        totalSteps,
        executionType: executionContext?.executionType,
      });

      const toolResponse = normalizeToolResponse(response, false);

      this.logger.debug('✅ [ResponseFormatter] Chain response formatted successfully');
      return toolResponse;
    } catch (error) {
      this.logger.error('❌ [ResponseFormatter] Chain response formatting failed', {
        chainId,
        error: error instanceof Error ? error.message : String(error),
      });

      return this.formatErrorResponse(error, executionContext);
    }
  }

  /**
   * Track execution for analytics
   */
  private trackExecution(executionContext: FormatterExecutionContext): void {
    try {
      if (this.analyticsService?.trackExecution) {
        this.analyticsService.trackExecution({
          executionId: executionContext.executionId,
          executionType: executionContext.executionType,
          duration: executionContext.endTime - executionContext.startTime,
          ...(executionContext.frameworkUsed !== undefined && {
            frameworkUsed: executionContext.frameworkUsed,
          }),
          ...(executionContext.stepsExecuted !== undefined && {
            stepsExecuted: executionContext.stepsExecuted,
          }),
          success: executionContext.success,
          ...(executionContext.sessionId !== undefined && {
            sessionId: executionContext.sessionId,
          }),
        });
      }
    } catch (error) {
      this.logger.warn('⚠️ [ResponseFormatter] Analytics tracking failed', {
        error: error instanceof Error ? error.message : String(error),
      });
    }
  }

  /**
   * Track error for analytics
   */
  private trackError(error: unknown, executionContext: FormatterExecutionContext): void {
    try {
      if (this.analyticsService?.trackError !== undefined) {
        this.analyticsService.trackError({
          executionId: executionContext.executionId,
          executionType: executionContext.executionType,
          errorType: error instanceof Error ? error.constructor.name : 'Unknown',
          errorMessage: error instanceof Error ? error.message : String(error),
          ...(executionContext.sessionId !== undefined && {
            sessionId: executionContext.sessionId,
          }),
        });
      }
    } catch (trackingError) {
      this.logger.warn('⚠️ [ResponseFormatter] Error analytics tracking failed', {
        error: trackingError instanceof Error ? trackingError.message : String(trackingError),
      });
    }
  }
}
