// @lifecycle canonical - Validates framework identifiers against the manager.
/**
 * Framework Validator
 * Centralized validation and normalization for framework identifiers.
 *
 * This class serves as the single entrypoint for verifying framework identifiers
 * across parsing, execution, and state-management components. All lookups are
 * delegated to the FrameworkManager to ensure a single source of truth.
 */
import { Logger } from '../../infra/logging/index.js';
import { ValidationError, type ErrorContext } from '../../shared/utils/errorHandling.js';

import type { FrameworkManager } from './framework-manager.js';
import type { FrameworkDefinition } from './types/index.js';

export interface FrameworkValidationOptions {
  /** Reject disabled frameworks when true */
  requireEnabled?: boolean;
  /** Optional identifier for logging/error context (ex: pipeline stage name) */
  stage?: string;
  /** Additional context merged into raised ValidationError instances */
  context?: Partial<ErrorContext>;
}

export interface FrameworkExistenceOptions {
  /** Only consider enabled frameworks */
  enabledOnly?: boolean;
}

export interface FrameworkValidationResult {
  normalizedId: string;
  definition: FrameworkDefinition;
}

interface FrameworkValidatorConfig {
  /** Default action label injected into ValidationError context */
  defaultStage?: string;
}

export class FrameworkValidator {
  private readonly defaultStage: string;

  constructor(
    private readonly frameworkManager: FrameworkManager,
    private readonly logger: Logger,
    config: FrameworkValidatorConfig = {}
  ) {
    this.defaultStage = config.defaultStage ?? 'framework_validation';
  }

  /**
   * Validate and normalize an identifier, returning the framework definition.
   * Throws ValidationError when identifier is missing, unknown, or disabled.
   */
  validateAndNormalize(
    frameworkId: string,
    options: FrameworkValidationOptions = {}
  ): FrameworkValidationResult {
    const normalizedId = this.tryNormalize(frameworkId);
    if (!normalizedId) {
      throw this.buildMissingFrameworkError(frameworkId, options);
    }

    const definition = this.frameworkManager.getFramework(normalizedId);
    if (!definition) {
      throw this.buildMissingFrameworkError(frameworkId, options);
    }

    if (options.requireEnabled && !definition.enabled) {
      throw this.buildDisabledFrameworkError(definition.id, options);
    }

    this.logger.debug('[FrameworkValidator] Framework validated', {
      requestedId: frameworkId,
      normalizedId: definition.id,
      requireEnabled: options.requireEnabled ?? false,
    });

    return {
      normalizedId: definition.id,
      definition,
    };
  }

  /**
   * Return true when the identifier exists in the registry.
   */
  exists(frameworkId: string, options: FrameworkExistenceOptions = {}): boolean {
    const normalizedId = this.tryNormalize(frameworkId);
    if (!normalizedId) {
      return false;
    }

    if (options.enabledOnly) {
      return this.frameworkManager.isFrameworkEnabled(normalizedId);
    }

    return Boolean(this.frameworkManager.getFramework(normalizedId));
  }

  /**
   * Try to normalize the identifier without raising errors.
   */
  tryNormalize(frameworkId: string | undefined | null): string | null {
    if (!frameworkId || typeof frameworkId !== 'string') {
      return null;
    }

    const trimmed = frameworkId.trim();
    if (!trimmed) {
      return null;
    }

    const validation = this.frameworkManager.validateIdentifier(trimmed);
    if (!validation.valid) {
      return null;
    }

    return validation.normalizedId ?? trimmed.toUpperCase();
  }

  private buildMissingFrameworkError(
    frameworkId: string,
    options: FrameworkValidationOptions
  ): ValidationError {
    const sanitized = frameworkId?.trim() ?? '';
    const availableFrameworks = this.frameworkManager.getFrameworkIds(false);
    const availableList = availableFrameworks.length
      ? availableFrameworks.join(', ')
      : 'none registered';

    const message = sanitized
      ? `Framework '@${sanitized}' not found. Available frameworks: ${availableList}. ` +
        `Use system_control({ action: "framework", operation: "list" }) for details.`
      : `Framework identifier is required. Available frameworks: ${availableList}. ` +
        `Use system_control({ action: "framework", operation: "list" }) for details.`;

    return new ValidationError(message, this.buildErrorContext(frameworkId, options));
  }

  private buildDisabledFrameworkError(
    frameworkId: string,
    options: FrameworkValidationOptions
  ): ValidationError {
    const message =
      `Framework '@${frameworkId}' is currently disabled. ` +
      `Use system_control({ action: "framework", operation: "list" }) to review enabled frameworks or re-enable it before retrying.`;

    const context = this.buildErrorContext(frameworkId, options);
    context.suggestions = [
      'Enable the framework through system_control before executing symbolic framework overrides',
      ...(context.suggestions || []),
    ];

    return new ValidationError(message, context);
  }

  private buildErrorContext(
    frameworkId: string,
    options: FrameworkValidationOptions
  ): ErrorContext {
    const availableFrameworks = this.frameworkManager.getFrameworkIds(true);
    const defaultSuggestions = [
      'Run system_control({ action: "framework", operation: "list" }) to review available frameworks',
      'Ensure the framework prefix matches the registry entry (e.g., @CAGEERF)',
    ];

    return {
      action: options.stage ?? this.defaultStage,
      userInput: { frameworkId },
      suggestions: options.context?.suggestions ?? defaultSuggestions,
      relatedComponents: ['framework-validator', 'framework-manager'],
      ...options.context,
      details: {
        availableFrameworks,
        ...(options.context?.details ?? {}),
      },
    };
  }
}
