'use strict';

const promptRuntime = require('../coding/prompt-runtime');

const PROMPT_ENVELOPE_SECTIONS = Object.freeze([
  'stable-policy',
  'workspace-persona',
  'project-context',
  'tool-policy',
  'provider-contribution',
  'channel-context',
  'parent-run-context',
  'child-scope',
  'runtime-only-context',
  'next-turn-context',
  'user-task',
]);

function buildRuntimePromptEnvelope(input = {}) {
  const envelope = promptRuntime.buildPromptEnvelope(input);
  return {
    ...envelope,
    cacheBoundary: computeCacheBoundary(envelope),
    diagnostics: validatePromptEnvelope(envelope),
  };
}

function computeCacheBoundary(envelope = {}) {
  const sections = envelope.sections || [];
  const firstDynamic = sections.findIndex(section => section.cacheable === false);
  return {
    stableSectionCount: firstDynamic === -1 ? sections.length : firstDynamic,
    stableTokenEstimate: sections
      .filter((section, index) => firstDynamic === -1 || index < firstDynamic)
      .reduce((sum, section) => sum + (section.tokenEstimate || 0), 0),
    dynamicSectionIds: sections
      .filter((section, index) => firstDynamic !== -1 && index >= firstDynamic)
      .map(section => section.id),
  };
}

function validatePromptEnvelope(envelope = {}) {
  const diagnostics = [];
  const seen = new Set();
  let sawDynamic = false;
  for (const section of envelope.sections || []) {
    if (seen.has(section.id)) {
      diagnostics.push({ level: 'warn', code: 'duplicate-section', sectionId: section.id });
    }
    seen.add(section.id);
    if (section.cacheable === false) sawDynamic = true;
    if (sawDynamic && section.cacheable === true) {
      diagnostics.push({ level: 'warn', code: 'cacheable-after-dynamic', sectionId: section.id });
    }
    if (section.id === 'stable-policy' && section.mutableByHooks !== false) {
      diagnostics.push({ level: 'error', code: 'stable-policy-mutable', sectionId: section.id });
    }
    if (section.source === 'provider' && section.id !== 'provider-contribution') {
      diagnostics.push({ level: 'warn', code: 'provider-section-not-isolated', sectionId: section.id });
    }
  }
  return diagnostics;
}

function applyPromptEnvelopePatch(envelope = {}, patch = {}) {
  const sections = [...(envelope.sections || [])];
  const diagnostics = [...(envelope.diagnostics || [])];
  for (const operation of patch.sections || []) {
    const id = operation.id;
    const index = sections.findIndex(section => section.id === id);
    if (index === -1) {
      diagnostics.push({ level: 'warn', code: 'patch-target-missing', sectionId: id });
      continue;
    }
    const current = sections[index];
    if (current.mutableByHooks === false) {
      diagnostics.push({ level: 'error', code: 'immutable-section-patch-blocked', sectionId: id });
      continue;
    }
    sections[index] = promptRuntime.normalizePromptSection({
      ...current,
      ...operation,
      content: operation.content ?? current.content,
      metadata: {
        ...(current.metadata || {}),
        ...(operation.metadata || {}),
      },
    });
  }
  const next = {
    ...envelope,
    sections,
    tokenEstimate: sections.reduce((sum, section) => sum + (section.tokenEstimate || 0), 0),
    metadata: {
      ...(envelope.metadata || {}),
      ...(patch.metadata || {}),
    },
  };
  next.cacheBoundary = computeCacheBoundary(next);
  next.diagnostics = [...diagnostics, ...validatePromptEnvelope(next)];
  return next;
}

function renderRuntimePromptEnvelope(envelope, options = {}) {
  return promptRuntime.renderPromptEnvelope(envelope, options);
}

module.exports = {
  ...promptRuntime,
  PROMPT_ENVELOPE_SECTIONS,
  applyPromptEnvelopePatch,
  buildRuntimePromptEnvelope,
  computeCacheBoundary,
  renderRuntimePromptEnvelope,
  validatePromptEnvelope,
};
