/**
 * MDAP Container Configuration - Tier-Based Resource Allocation
 *
 * Maps MDAP model tiers to Docker container resource requirements
 * (memory, CPU, timeout) for intelligent tier-based scaling.
 *
 * @module mdap-container-config
 * @version 1.0.0
 */

import { ContainerResult } from './docker-spawner.js';

// =============================================
// Type Definitions
// =============================================

/**
 * Container resources for a given tier
 */
export interface MdapContainerConfig {
  /** Memory allocation string (e.g., "512m", "1g", "2g") */
  memoryString: string;
  /** Memory in bytes */
  memoryBytes: number;
  /** CPU cores (e.g., 0.5, 1, 2) */
  cpus: number;
  /** Execution timeout in milliseconds */
  timeout: number;
  /** Tier level (1-5) */
  tier: number;
}

/**
 * Escalation decision based on execution result
 */
export interface EscalationDecision {
  /** Whether to escalate to next tier */
  shouldEscalate: boolean;
  /** Reason for escalation decision */
  reason?: string;
  /** Recommended next tier (if escalating) */
  nextTier?: number;
}

// =============================================
// Tier Configuration
// =============================================

/**
 * Memory and timeout allocation by tier
 *
 * Conservative scaling strategy:
 * - Tier 1: Minimal resources for simple tasks
 * - Tier 2: Moderate resources for standard tasks
 * - Tier 3: Higher resources for complex tasks
 * - Tier 4: Substantial resources for difficult tasks
 * - Tier 5: Maximum resources for critical tasks
 */
const TIER_CONFIG: Record<number, Omit<MdapContainerConfig, 'memoryBytes'>> = {
  1: {
    memoryString: '512m',
    cpus: 0.5,
    timeout: 300000, // 5 minutes
    tier: 1,
  },
  2: {
    memoryString: '1g',
    cpus: 1,
    timeout: 600000, // 10 minutes
    tier: 2,
  },
  3: {
    memoryString: '2g',
    cpus: 2,
    timeout: 900000, // 15 minutes
    tier: 3,
  },
  4: {
    memoryString: '4g',
    cpus: 2,
    timeout: 1200000, // 20 minutes
    tier: 4,
  },
  5: {
    memoryString: '8g',
    cpus: 4,
    timeout: 1800000, // 30 minutes
    tier: 5,
  },
};

// =============================================
// Memory String Parsing
// =============================================

/**
 * Parse memory string to bytes
 *
 * Supports: b, k/kb, m/mb, g/gb (case-insensitive)
 * Examples: "512m" -> 536870912, "1g" -> 1073741824
 *
 * @param memoryString Memory specification string
 * @returns Memory in bytes
 */
function parseMemoryStringToBytes(memoryString: string): number {
  const match = memoryString.trim().toLowerCase().match(/^(\d+(?:\.\d+)?)\s*([a-z]*)$/);

  if (!match) {
    throw new Error(
      `[mdap-container-config] Invalid memory format: "${memoryString}". Expected: "512m", "1g", etc.`
    );
  }

  const [, value, unit] = match;
  const num = parseFloat(value);

  if (isNaN(num) || num <= 0) {
    throw new Error(`[mdap-container-config] Memory value must be positive: ${num}`);
  }

  const multipliers: Record<string, number> = {
    b: 1,
    '': 1,
    k: 1024,
    kb: 1024,
    m: 1024 * 1024,
    mb: 1024 * 1024,
    g: 1024 * 1024 * 1024,
    gb: 1024 * 1024 * 1024,
  };

  const multiplier = multipliers[unit];
  if (multiplier === undefined) {
    throw new Error(
      `[mdap-container-config] Unknown memory unit: "${unit}". Supported: b, k/kb, m/mb, g/gb`
    );
  }

  return Math.round(num * multiplier);
}

// =============================================
// Public Functions
// =============================================

/**
 * Get container resource configuration for a given tier
 *
 * Returns memory, CPU, and timeout settings based on the tier.
 * Clamps invalid tier values to valid range (1-5).
 *
 * @param modelTier Model tier (1-5)
 * @returns Container configuration for the tier
 */
export function getContainerResourcesForTier(modelTier?: number): MdapContainerConfig {
  // Default to tier 2 if not provided
  const tier = Math.max(1, Math.min(5, modelTier ?? 2));

  const config = TIER_CONFIG[tier];
  if (!config) {
    throw new Error(`[mdap-container-config] Invalid tier: ${tier}`);
  }

  return {
    ...config,
    memoryBytes: parseMemoryStringToBytes(config.memoryString),
  };
}

/**
 * Determine if a container execution should be escalated to a higher tier
 *
 * Escalation logic:
 * - OOM killed (exit code 137): ESCALATE
 * - Timeout (exit code 124 or durationMs >= timeout): ESCALATE
 * - Non-zero exit code (other): ESCALATE
 * - Success (exit code 0): DO NOT ESCALATE
 *
 * @param result Container execution result
 * @param currentTier Current model tier (1-5)
 * @returns Escalation decision with reason
 */
export function shouldEscalate(result: ContainerResult, currentTier?: number): EscalationDecision {
  const tier = Math.max(1, Math.min(5, currentTier ?? 1));

  // If execution succeeded, no escalation needed
  if (result.success || result.exitCode === 0) {
    return {
      shouldEscalate: false,
      reason: 'Execution succeeded',
    };
  }

  // Already at max tier, cannot escalate further
  if (tier >= 5) {
    return {
      shouldEscalate: false,
      reason: 'Already at maximum tier (5)',
    };
  }

  // OOM killed (exit code 137) - definitely escalate
  if (result.exitCode === 137) {
    return {
      shouldEscalate: true,
      reason: 'Out of memory (exit code 137)',
      nextTier: Math.min(5, tier + 1),
    };
  }

  // Timeout (exit code 124)
  if (result.exitCode === 124) {
    return {
      shouldEscalate: true,
      reason: 'Execution timeout (exit code 124)',
      nextTier: Math.min(5, tier + 1),
    };
  }

  // General failure - escalate
  return {
    shouldEscalate: true,
    reason: `Container failed with exit code ${result.exitCode}`,
    nextTier: Math.min(5, tier + 1),
  };
}

/**
 * Extract memory peak usage from container logs/stderr
 *
 * Docker stats typically appear in container stderr with format:
 * "Memory: 256.5 MB" or similar patterns
 *
 * @param result Container execution result
 * @returns Peak memory usage in bytes (null if cannot be extracted)
 */
export function extractMemoryPeak(result: ContainerResult): number | null {
  // Try to parse memory info from stderr or stdout
  const output = result.stderr || result.stdout;

  // Look for patterns like "Memory: 256.5 MB" or "memory=256MB"
  const memoryMatch = output.match(/memory[:\s=]+([0-9.]+)\s*(?:gb|mb|kb|b)?/i);
  if (memoryMatch) {
    const value = parseFloat(memoryMatch[1]);
    if (!isNaN(value)) {
      // Assume MB if no unit specified
      return Math.round(value * 1024 * 1024);
    }
  }

  return null;
}

/**
 * Extract CPU time from container logs
 *
 * Docker stats may include CPU info. This is a placeholder for actual
 * CPU time extraction from container runtime statistics.
 *
 * @param result Container execution result
 * @returns CPU time in milliseconds (null if cannot be extracted)
 */
export function extractCpuTime(result: ContainerResult): number | null {
  // Try to parse CPU info from stderr or stdout
  const output = result.stderr || result.stdout;

  // Look for patterns like "CPU: 1234ms" or "cpu=1234"
  const cpuMatch = output.match(/cpu[:\s=]+([0-9.]+)\s*(?:ms|s)?/i);
  if (cpuMatch) {
    const value = parseFloat(cpuMatch[1]);
    if (!isNaN(value)) {
      return Math.round(value);
    }
  }

  return null;
}

/**
 * Calculate memory usage percentage based on peak usage and limit
 *
 * @param peakMemoryBytes Peak memory used in bytes
 * @param limitBytes Memory limit in bytes
 * @returns Usage percentage (0-100)
 */
export function calculateMemoryUsagePercent(peakMemoryBytes: number | null, limitBytes: number): number {
  if (peakMemoryBytes === null || limitBytes <= 0) {
    return 0;
  }

  return Math.min(100, Math.round((peakMemoryBytes / limitBytes) * 100));
}

/**
 * Validate tier value and clamp to valid range
 *
 * @param tier Tier value to validate
 * @returns Validated tier (1-5)
 */
export function validateTier(tier: number | undefined): number {
  if (tier === undefined) {
    return 2; // Default to tier 2
  }

  return Math.max(1, Math.min(5, Math.floor(tier)));
}

/**
 * Get tier name for display
 *
 * @param tier Tier number (1-5)
 * @returns Human-readable tier name
 */
export function getTierName(tier: number): string {
  const tierNames: Record<number, string> = {
    1: 'Haiku (Fast)',
    2: 'Standard',
    3: 'Advanced',
    4: 'Expert',
    5: 'Maximum',
  };

  return tierNames[Math.max(1, Math.min(5, tier))] || 'Unknown';
}
