#!/usr/bin/env node
/**
 * OpenClaw Context — Content Type Detector
 *
 * Auto-detect content type to select optimal compression strategy.
 * Migrated from Headroom `crates/headroom-core/src/transforms/content_detector.rs`
 *
 * Usage:
 *   node src/compression/detector.js < content.txt
 *   → { type: "json_array", confidence: 0.95 }
 */

// ─── Content Types ───────────────────────────────────────────────

const CONTENT_TYPES = {
  JSON_ARRAY:       'json_array',
  JSON_OBJECT:      'json_object',
  BUILD_OUTPUT:     'build_output',
  SEARCH_RESULTS:   'search_results',
  GIT_DIFF:         'git_diff',
  CODE_BLOCK:       'code_block',
  LOG_OUTPUT:       'log_output',
  PLAIN_TEXT:       'plain_text',
  MIXED:            'mixed',
};

// ─── Signal Weights ──────────────────────────────────────────────

const SIGNALS = {
  // JSON signals — must be valid JSON to get full weight
  json_start:        { weight: 0.3, test: s => { try { const p = JSON.parse(s); return Array.isArray(p) || (typeof p === 'object' && p !== null); } catch { return false; }}},
  json_array_start:  { weight: 0.6, test: s => { try { const p = JSON.parse(s); return Array.isArray(p); } catch { return false; }}},

  // Build output signals (stronger to beat false JSON detection)
  build_error:       { weight: 0.7, test: s => /error[:\s]/i.test(s) },
  build_warning:     { weight: 0.5, test: s => /warning[:\s]/i.test(s) },
  build_success:     { weight: 0.4, test: s => /BUILD SUCCESS|passed/i.test(s) },
  compilation_unit:  { weight: 0.6, test: s => /\bCompiling\b|Building\b|\[.*compil/i.test(s) },
  log_timestamp_bracketed: { weight: 0.5, test: s => /^\[\d{4}[-/]\d{2}/m.test(s) },
  test_output:       { weight: 0.5, test: s => /\b(PASS|FAIL|✓|✗|✕)\b|\btests?\s+\d+.*(passed|failed)/i.test(s) },
  npm_output:        { weight: 0.4, test: s => /\bnpm\b|yarn\b|pnpm\b/.test(s) },
  cargo_output:      { weight: 0.4, test: s => /^\s+(Compiling|Downloading|Finished)/m.test(s) },

  // Search result signals
  search_line:       { weight: 0.6, test: s => /^[^:\n]+:\d+:/.test(s) || /^\S+:\d+:\d+/.test(s) },
  grep_like:         { weight: 0.5, test: s => /^[^:]+\.[a-z]+:\d+:/mi.test(s) },
  filename_prefix:   { weight: 0.3, test: s => /^(\S+\/)*\S+\.\w{1,6}:/m.test(s) },

  // Git diff signals
  diff_header:       { weight: 0.8, test: s => /^diff --git /m.test(s) },
  diff_index:        { weight: 0.6, test: s => /^index [0-9a-f]+/m.test(s) },
  diff_hunk:         { weight: 0.5, test: s => /^@@\s+-\d+.*\+\d+.*@@/m.test(s) },

  // Log signals
  log_timestamp:     { weight: 0.4, test: s => /\d{4}[-/]\d{2}[-/]\d{2}[T ]\d{2}:\d{2}:\d{2}/.test(s) },
  log_level:         { weight: 0.3, test: s => /\b(DEBUG|INFO|WARN|ERROR|FATAL|TRACE)\b/.test(s) },
  stack_trace:       { weight: 0.4, test: s => /\bat\s+\S+\.\S+:\d+:\d+/.test(s) },

  // Code signals
  code_keywords:     { weight: 0.4, test: s => /\b(function|class|import|export|const|let|var|def|fn|pub|void|int|string)\b/.test(s) },
  code_comment:      { weight: 0.2, test: s => /^\s*(\/\/|#|\/\*|-->|\*)\s/.test(s) },

  // Plain text signals
  prose_sentence:    { weight: 0.2, test: s => /[A-Z][a-z]+.*[.!?]\s+[A-Z]/.test(s) },
};

// ─── Detection Logic ─────────────────────────────────────────────

/**
 * Score each content type based on signal matches
 */
function scoreContent(text) {
  const scores = {};

  for (const [name, signal] of Object.entries(SIGNALS)) {
    if (signal.test(text)) {
      // Map signal to type
      const type = signalType(name);
      scores[type] = (scores[type] || 0) + signal.weight;
    }
  }

  return scores;
}

/**
 * Map signal name to content type category
 */
function signalType(name) {
  if (name.startsWith('json_'))        return CONTENT_TYPES.JSON_ARRAY;
  if (name.startsWith('build_') || name.startsWith('compile_') || name.startsWith('log_') || name.includes('_output') || name === 'test_output')
    return CONTENT_TYPES.BUILD_OUTPUT;
  if (name.startsWith('search_') || name.startsWith('grep_') || name === 'filename_prefix')
    return CONTENT_TYPES.SEARCH_RESULTS;
  if (name.startsWith('diff_'))        return CONTENT_TYPES.GIT_DIFF;
  if (name.startsWith('log_') || name === 'stack_trace')
    return CONTENT_TYPES.LOG_OUTPUT;
  if (name.startsWith('code_'))        return CONTENT_TYPES.CODE_BLOCK;
  return CONTENT_TYPES.PLAIN_TEXT;
}

/**
 * Detect content type with confidence score
 */
function detectType(text) {
  if (!text || text.trim().length === 0) {
    return { type: CONTENT_TYPES.PLAIN_TEXT, confidence: 1.0, size: 0 };
  }

  const scores = scoreContent(text);

  // Fast path: valid JSON (but only if text looks structural, not a log line)
  if (scores[CONTENT_TYPES.JSON_ARRAY] > 0.5 && text.trim().startsWith('[')) {
    try {
      const parsed = JSON.parse(text);
      if (Array.isArray(parsed)) {
        return { type: CONTENT_TYPES.JSON_ARRAY, confidence: 0.95, size: text.length };
      }
      if (typeof parsed === 'object') {
        return { type: CONTENT_TYPES.JSON_OBJECT, confidence: 0.90, size: text.length };
      }
    } catch {}
  }

  // Find highest scoring type
  let bestType = CONTENT_TYPES.PLAIN_TEXT;
  let bestScore = 0;

  for (const [type, score] of Object.entries(scores)) {
    if (score > bestScore) {
      bestScore = score;
      bestType = type;
    }
  }

  // Confidence clamp
  const confidence = Math.min(1.0, bestScore / 1.5);

  return {
    type: bestType,
    confidence: Math.round(confidence * 100) / 100,
    size: text.length,
    scores: Object.entries(scores).filter(([,s]) => s > 0),
  };
}

// ─── Format Size ─────────────────────────────────────────────────

function formatSize(bytes) {
  if (bytes < 1024) return `${bytes}B`;
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
  return `${(bytes / 1024 / 1024).toFixed(1)}MB`;
}

// ─── CLI ─────────────────────────────────────────────────────────

function main() {
  const args = process.argv.slice(2);

  if (args.length > 0 && (args[0] === '--help' || args[0] === '-h')) {
    console.log(`Usage: node detector.js [--json] <file>
       cat content.txt | node detector.js
       
Output: { type, confidence, size }
Types:  json_array | json_object | build_output | search_results | 
        git_diff | log_output | code_block | plain_text | mixed`);
    return;
  }

  // Read from stdin
  let input = '';
  process.stdin.setEncoding('utf8');
  process.stdin.on('data', chunk => input += chunk);
  process.stdin.on('end', () => {
    const result = detectType(input);
    result.size_formatted = formatSize(result.size);
    console.log(JSON.stringify(result, null, 2));
  });
}

// ─── Module exports ──────────────────────────────────────────────

module.exports = { detectType, CONTENT_TYPES, formatSize };

if (require.main === module) {
  main();
}
