'use strict';

const crypto = require('crypto');

const STICKY_ATTENTION_MS = 12 * 60 * 60 * 1000;
const HARD_FAILURE_RE = /\b(cannot proceed|blocked|blocker|stuck|permission denied|fatal|panic|segmentation fault|uncaught|unhandled exception|traceback|command failed|build failed|tests? failed|npm err!|failed to run step|exceeded max retries|max retries|something went wrong)\b/i;
const HARD_FAILURE_EVIDENCE_RE = /(cannot proceed|blocked|blocker|stuck|permission denied|fatal|unhandled exception|command failed|build failed|tests? failed|failed to run step|exceeded max retries|max retries|something went wrong)/i;

function compactText(value) {
  if (!value) return '';
  if (typeof value === 'string') return value.replace(/\s+/g, ' ').trim();
  if (typeof value === 'object') {
    return compactText(value.text || value.summary || value.phase || value.next || value.reason || '');
  }
  return String(value).replace(/\s+/g, ' ').trim();
}

function truncateText(value, max = 700) {
  const text = compactText(value);
  if (text.length <= max) return text;
  return `${text.slice(0, Math.max(0, max - 1)).trim()}...`;
}

function normalizeSeverity(value) {
  const text = String(value || '').toLowerCase().trim();
  if (text === 'failure' || text === 'fail' || text === 'blocked' || text === 'blocker') return 'failure';
  if (text === 'warning' || text === 'warn' || text === 'caution' || text === 'risk') return 'warning';
  return 'none';
}

function normalizeConfidence(value) {
  const text = String(value || '').toLowerCase().trim();
  if (text === 'high' || text === 'medium' || text === 'low') return text;
  return 'medium';
}

function normalizeAttention(value, defaults = {}) {
  if (!value || typeof value !== 'object') return null;
  const severity = normalizeSeverity(value.severity || value.level || value.kind);
  const recommendation = truncateText(
    value.recommendation || value.reason || value.summary || defaults.recommendation || '',
    220
  );
  const evidence = Array.isArray(value.evidence)
    ? value.evidence.map(item => truncateText(item, 100)).filter(Boolean).slice(0, 3)
    : [];
  return {
    severity,
    actionLabel: severity === 'failure' ? 'Inspect' : severity === 'warning' ? 'Review' : '',
    recommendation: recommendation || defaultRecommendation(severity),
    confidence: normalizeConfidence(value.confidence || defaults.confidence),
    evidence,
    source: value.source || defaults.source || 'unknown',
    model: value.model || defaults.model || '',
    updatedAt: value.updatedAt || defaults.updatedAt || null,
    sticky: !!value.sticky,
  };
}

function defaultRecommendation(severity) {
  if (severity === 'failure') return 'A current blocker or failure appears to need attention.';
  if (severity === 'warning') return 'A current warning appears to need attention.';
  return '';
}

function attentionContextHash(text) {
  return crypto.createHash('sha1').update(String(text || '')).digest('hex');
}

function buildStandupAttentionContext({ session = {}, summary = {}, status = {} } = {}) {
  const evidence = Array.isArray(summary.statusEvidence) ? summary.statusEvidence : [];
  const progress = summary.progress || {};
  const terminalAttentionText = summary.terminalAttentionText || summary.terminal_attention_text || session.terminalAttentionText || '';
  const lines = [
    `Title: ${compactText(session.label || summary.displayPrompt || summary.lastPrompt || session.id || '')}`,
    `Agent: ${compactText(session.agentType || session.agent || session.type || '')}`,
    `Status: ${compactText(session.standupStatus || session.liveStatus || session.serverState || status.status || summary.status || '')}`,
    `Waiting: ${compactText(session.waitingReason || status.reason || '')}`,
    `Intent: ${compactText(summary.intent || summary.displayPrompt || summary.lastPrompt || '')}`,
    `Summary: ${compactText(summary.summary || '')}`,
    `Progress: ${compactText(progress)}`,
    `Evidence: ${evidence.map(compactText).filter(Boolean).join('; ')}`,
    `Terminal: ${compactText(terminalAttentionText)}`,
  ].filter(line => !/:\s*$/.test(line));
  const text = truncateText(lines.join('\n'), 2500);
  return {
    text,
    hash: attentionContextHash(text),
    hasAttentionLanguage: hasAttentionLanguage(text),
  };
}

function hasAttentionLanguage(text) {
  return /\b(warn(?:ing|ed)?|caution|risk|blocked|blocker|failed|failing|failure|error|exception|permission denied|cannot proceed|stuck|needs attention|exceeded max retries|max retries|something went wrong)\b/i.test(String(text || ''));
}

function hasResolutionSignal(text) {
  return /\b(resolved|fixed|cleared|no longer|not blocked|not a blocker|no blockers?|not a failure|no failures?|not an error|no errors?|no warnings?|unblocked|passed|verified|working now|succeeded|successful|recovered)\b/i.test(String(text || ''));
}

function heuristicStandupAttention(context) {
  const text = String(context?.text || context || '');
  const lower = text.toLowerCase();
  const resolved = hasResolutionSignal(lower);
  const hardFailure = HARD_FAILURE_RE.test(lower);
  const warning = /\b(warn(?:ing|ed)?|caution|risk|heads up|possible issue|needs attention)\b/i.test(lower);
  const weakFailure = /\b(failed|failing|failure|error|exception)\b/i.test(lower);

  if (resolved && !warning && !hardFailure) {
    return normalizeAttention({ severity: 'none', source: 'heuristic', confidence: 'medium' });
  }
  if (hardFailure && !resolved) {
    return normalizeAttention({
      severity: 'failure',
      recommendation: 'A current blocker or failure appears to need attention.',
      evidence: [firstMatchingPhrase(text, HARD_FAILURE_EVIDENCE_RE)],
      source: 'heuristic',
      confidence: 'medium',
    });
  }
  if (warning) {
    return normalizeAttention({
      severity: 'warning',
      recommendation: 'A current warning appears to need attention.',
      evidence: [firstMatchingPhrase(text, /(warn(?:ing|ed)?|caution|risk|possible issue|needs attention)/i)],
      source: 'heuristic',
      confidence: 'medium',
    });
  }
  if (weakFailure && !resolved) {
    return normalizeAttention({
      severity: 'warning',
      recommendation: 'Failure or error language appears in the latest context; review whether it is still relevant.',
      evidence: [firstMatchingPhrase(text, /(failed|failing|failure|error|exception)/i)],
      source: 'heuristic',
      confidence: 'low',
    });
  }
  return normalizeAttention({ severity: 'none', source: 'heuristic', confidence: 'medium' });
}

function firstMatchingPhrase(text, regex) {
  const match = String(text || '').match(regex);
  return match ? match[0] : '';
}

function parseStandupAttentionResult(text, defaults = {}) {
  const raw = String(text || '').trim();
  if (!raw) return null;
  const cleaned = raw
    .replace(/^```(?:json)?\s*/i, '')
    .replace(/\s*```$/i, '')
    .trim();
  const start = cleaned.indexOf('{');
  const end = cleaned.lastIndexOf('}');
  if (start === -1 || end === -1 || end <= start) return null;
  try {
    return normalizeAttention(JSON.parse(cleaned.slice(start, end + 1)), defaults);
  } catch {
    return null;
  }
}

function mergeStickyStandupAttention(previous, next, contextText, now = Date.now(), opts = {}) {
  const stickyMs = Number.isFinite(opts.stickyMs) ? opts.stickyMs : STICKY_ATTENTION_MS;
  const normalizedNext = normalizeAttention(next, { updatedAt: new Date(now).toISOString() })
    || normalizeAttention({ severity: 'none', source: 'none' }, { updatedAt: new Date(now).toISOString() });
  const normalizedPrevious = normalizeAttention(previous);

  if (normalizedNext.severity === 'failure' || normalizedNext.severity === 'warning') {
    return {
      ...normalizedNext,
      updatedAt: normalizedNext.updatedAt || new Date(now).toISOString(),
      sticky: false,
    };
  }

  if (!normalizedPrevious || !['failure', 'warning'].includes(normalizedPrevious.severity)) {
    return normalizedNext;
  }

  if (hasResolutionSignal(contextText)) return normalizedNext;

  const previousMs = Date.parse(normalizedPrevious.updatedAt || '') || now;
  if (now - previousMs > stickyMs) return normalizedNext;

  return {
    ...normalizedPrevious,
    updatedAt: normalizedPrevious.updatedAt || new Date(now).toISOString(),
    sticky: true,
  };
}

module.exports = {
  STICKY_ATTENTION_MS,
  buildStandupAttentionContext,
  hasAttentionLanguage,
  hasResolutionSignal,
  heuristicStandupAttention,
  mergeStickyStandupAttention,
  normalizeAttention,
  normalizeSeverity,
  parseStandupAttentionResult,
};
