const { merkleProof, merkleRoot, sha256 } = require('./hash');

const FRAGMENT_REGISTRY_VERSION = 'mazex.fragment_registry.v1';
const MAX_FRAGMENT_PAYLOAD_BYTES = 4096;
const MAX_FRAGMENT_NAME_BYTES = 96;
const MAX_QUARANTINE_EVIDENCE_BYTES = 4096;
const MAX_FRAGMENT_VERSION_BYTES = 32;
const MAX_FRAGMENT_DOWNLOAD_LIMIT = 1000;
const FRAGMENT_KINDS = new Set([
  'operator',
  'static_parameter',
  'dynamic_parameter',
  'composition',
  'verifier_constraint',
  'domain_rule',
  'behavior_modifier',
]);
const QUARANTINE_REASON_CODES = new Set([
  'format_violation',
  'nondeterministic',
  'unsafe_execution',
  'budget_violation',
  'compatibility_violation',
  'duplicate_content',
  'duplicate_behavior',
  'invalid_domain',
  'invalid_composition',
  'protocol_bug',
]);
const ALLOWED_DYNAMIC_CONTEXT_SOURCES = new Set([
  'time_bucket',
  'click_count',
  'last_point',
  'chain_state',
  'layer_no',
  'step_no',
  'grid_size',
  'previous_behavior_root',
]);

function isFiniteNumber(value) {
  return Number.isFinite(Number(value));
}

function isNonEmptyArray(value) {
  return Array.isArray(value) && value.length > 0;
}

function validateRange(range, errors, label = 'range') {
  if (!Array.isArray(range) || range.length !== 2 || !isFiniteNumber(range[0]) || !isFiniteNumber(range[1])) {
    errors.push(`${label}_invalid`);
    return;
  }
  if (Number(range[0]) > Number(range[1])) errors.push(`${label}_reversed`);
}

const DEFAULT_FRAGMENTS = [
  {
    kind: 'operator',
    name: 'base1.operator_unit',
    payload: {
      deterministic: true,
      allowed_scope: ['u_expr', 'v_expr'],
      safety: 'operator must bind one valid parameter unit before insertion',
    },
  },
  {
    kind: 'static_parameter',
    name: 'base1.nonzero_integer',
    payload: {
      deterministic: true,
      zero_allowed: false,
      range_policy: 'operator-specific-safe-range',
    },
  },
  {
    kind: 'dynamic_parameter',
    name: 'base1.context_parameter',
    payload: {
      deterministic: true,
      context_sources: ['time_bucket', 'click_count', 'last_point', 'chain_state'],
    },
  },
  {
    kind: 'composition',
    name: 'base1.expression_tree_insert',
    payload: {
      deterministic: true,
      rule: 'operator units may be inserted into any legal expression-tree node',
    },
  },
  {
    kind: 'verifier_constraint',
    name: 'base1.answer_budget',
    payload: {
      deterministic: true,
      checks: ['tree_nodes', 'tree_depth', 'operator_units', 'expression_bytes'],
    },
  },
  {
    kind: 'domain_rule',
    name: 'base1.grid_domain',
    payload: {
      deterministic: true,
      minimum_grid: 20,
      output: 'normalize every coordinate into [0, N)',
    },
  },
];

function cloneJson(value) {
  return JSON.parse(JSON.stringify(value));
}

function byteLength(value) {
  return Buffer.byteLength(typeof value === 'string' ? value : JSON.stringify(value || {}), 'utf8');
}

function normalizeKind(kind) {
  return String(kind || '').trim().toLowerCase();
}

function normalizeFragmentInput(input = {}) {
  const fragment = input.fragment && typeof input.fragment === 'object' ? input.fragment : input;
  return {
    kind: normalizeKind(fragment.kind || fragment.type),
    name: String(fragment.name || '').trim(),
    version: String(fragment.version || '1.0.0').trim(),
    author: String(fragment.author || fragment.owner || 'genesis').trim(),
    supersedes_fragment_id: String(
      fragment.supersedes_fragment_id || fragment.supersedesFragmentId || fragment.supersedes || '',
    ).trim(),
    payload: cloneJson(fragment.payload || fragment.content || {}),
    budget: {
      max_payload_bytes: Math.trunc(Number(fragment.budget?.max_payload_bytes || MAX_FRAGMENT_PAYLOAD_BYTES)),
      min_layer: Math.max(1, Math.trunc(Number(fragment.budget?.min_layer || fragment.min_layer || 1))),
      score: Math.max(1, Math.trunc(Number(fragment.budget?.score || fragment.score || 1))),
    },
    deterministic: fragment.deterministic !== false,
    compatibility: {
      protocol: String(fragment.compatibility?.protocol || fragment.protocol || 'base1').trim(),
      min_version: String(fragment.compatibility?.min_version || '1.0.0').trim(),
    },
  };
}

function validateFragmentKindSpecific(fragment, payloadBytes) {
  const errors = [];
  const payload = fragment.payload || {};
  switch (fragment.kind) {
    case 'operator': {
      if (!payload.operator_id && !payload.op && !payload.rule && !payload.safety) errors.push('operator_rule_required');
      if (payload.arity !== undefined && (!Number.isInteger(Number(payload.arity)) || Number(payload.arity) < 1)) {
        errors.push('operator_arity_invalid');
      }
      if (payload.safe_range !== undefined) validateRange(payload.safe_range, errors, 'operator_safe_range');
      break;
    }
    case 'static_parameter': {
      const hasValue = Object.prototype.hasOwnProperty.call(payload, 'value');
      const hasRange = Object.prototype.hasOwnProperty.call(payload, 'range');
      if (!hasValue && !hasRange && !payload.range_policy) errors.push('static_parameter_value_or_range_required');
      if (hasValue && !isFiniteNumber(payload.value)) errors.push('static_parameter_value_invalid');
      if (hasValue && Number(payload.value) === 0 && payload.zero_allowed === false) {
        errors.push('static_parameter_zero_not_allowed');
      }
      if (hasRange) validateRange(payload.range, errors, 'static_parameter_range');
      break;
    }
    case 'dynamic_parameter': {
      const sources = payload.context_sources || payload.sources;
      if (!isNonEmptyArray(sources)) {
        errors.push('dynamic_context_sources_required');
      } else {
        for (const source of sources) {
          if (!ALLOWED_DYNAMIC_CONTEXT_SOURCES.has(String(source))) errors.push(`dynamic_context_source_not_allowed:${source}`);
        }
      }
      if (payload.range !== undefined) validateRange(payload.range, errors, 'dynamic_parameter_range');
      break;
    }
    case 'composition': {
      if (!payload.rule && !payload.insertion_policy && !payload.strategy) errors.push('composition_rule_required');
      if (payload.max_depth !== undefined && (!Number.isInteger(Number(payload.max_depth)) || Number(payload.max_depth) < 1)) {
        errors.push('composition_max_depth_invalid');
      }
      if (payload.max_nodes !== undefined && (!Number.isInteger(Number(payload.max_nodes)) || Number(payload.max_nodes) < 1)) {
        errors.push('composition_max_nodes_invalid');
      }
      break;
    }
    case 'verifier_constraint': {
      if (!isNonEmptyArray(payload.checks) && !payload.answer_budget && !payload.constraint) {
        errors.push('constraint_checks_required');
      }
      if (payload.relaxes_genesis_protocol === true || payload.relaxes === true) {
        errors.push('constraint_cannot_relax_genesis_protocol');
      }
      break;
    }
    case 'domain_rule': {
      if (payload.minimum_grid !== undefined && (!Number.isInteger(Number(payload.minimum_grid)) || Number(payload.minimum_grid) < 1)) {
        errors.push('domain_minimum_grid_invalid');
      }
      if (payload.maximum_grid !== undefined && (!Number.isInteger(Number(payload.maximum_grid)) || Number(payload.maximum_grid) < 1)) {
        errors.push('domain_maximum_grid_invalid');
      }
      if (!payload.output && !payload.normalization && !payload.domain) errors.push('domain_rule_required');
      break;
    }
    case 'behavior_modifier': {
      if (!payload.rule && !payload.modifier_id && !payload.behavior) errors.push('behavior_modifier_rule_required');
      if (payload.stateful === true) {
        const sources = payload.context_sources || payload.sources;
        if (!isNonEmptyArray(sources)) errors.push('behavior_modifier_context_sources_required');
      }
      break;
    }
    default:
      break;
  }
  if (payloadBytes > fragment.budget.max_payload_bytes) errors.push('budget_payload_limit_exceeded');
  if (fragment.supersedes_fragment_id && !/^0x[0-9a-f]{64}$/.test(fragment.supersedes_fragment_id)) {
    errors.push('supersedes_fragment_id_invalid');
  }
  if (byteLength(fragment.version) > MAX_FRAGMENT_VERSION_BYTES) errors.push('version_too_large');
  return errors;
}

function fragmentBehaviorFingerprint(fragment) {
  const payload = fragment.payload || {};
  return sha256({
    type: 'mazex.fragment.behavior_fingerprint',
    version: FRAGMENT_REGISTRY_VERSION,
    kind: fragment.kind,
    payload,
    compatibility: fragment.compatibility,
  });
}

function validateFragmentInput(input = {}) {
  const fragment = normalizeFragmentInput(input);
  const errors = [];
  if (!FRAGMENT_KINDS.has(fragment.kind)) errors.push('kind_not_allowed');
  if (!fragment.name) errors.push('name_required');
  if (byteLength(fragment.name) > MAX_FRAGMENT_NAME_BYTES) errors.push('name_too_large');
  if (!fragment.version) errors.push('version_required');
  if (!fragment.author) errors.push('author_required');
  if (!fragment.deterministic) errors.push('fragment_must_be_deterministic');
  const payloadBytes = byteLength(fragment.payload);
  if (payloadBytes <= 0) errors.push('payload_required');
  if (payloadBytes > Math.min(fragment.budget.max_payload_bytes, MAX_FRAGMENT_PAYLOAD_BYTES)) {
    errors.push('payload_too_large');
  }
  if (!fragment.compatibility.protocol) errors.push('protocol_required');
  errors.push(...validateFragmentKindSpecific(fragment, payloadBytes));
  return {
    ok: errors.length === 0,
    errors,
    fragment,
    payload_bytes: payloadBytes,
    behavior_fingerprint: fragmentBehaviorFingerprint(fragment),
  };
}

function normalizeQuarantineInput(input = {}) {
  const source = input.quarantine && typeof input.quarantine === 'object' ? input.quarantine : input;
  return {
    fragment_id: String(source.fragment_id || source.fragmentId || '').trim(),
    challenger: String(source.challenger || source.reporter || source.author || 'fragment-challenger').trim(),
    reason_code: String(source.reason_code || source.reasonCode || source.reason || '').trim().toLowerCase(),
    evidence: cloneJson(source.evidence || source.proof || {}),
    created_at: source.created_at || source.createdAt || null,
  };
}

function validateQuarantineInput(input = {}) {
  const quarantine = normalizeQuarantineInput(input);
  const errors = [];
  if (!/^0x[0-9a-f]{64}$/.test(quarantine.fragment_id)) errors.push('fragment_id_invalid');
  if (!quarantine.challenger) errors.push('challenger_required');
  if (!QUARANTINE_REASON_CODES.has(quarantine.reason_code)) errors.push('reason_code_not_allowed');
  const evidenceBytes = byteLength(quarantine.evidence);
  if (evidenceBytes <= 0) errors.push('evidence_required');
  if (evidenceBytes > MAX_QUARANTINE_EVIDENCE_BYTES) errors.push('evidence_too_large');
  return {
    ok: errors.length === 0,
    errors,
    quarantine,
    evidence_bytes: evidenceBytes,
  };
}

function fragmentContentHash(fragment) {
  return sha256({
    type: 'mazex.fragment.content',
    version: FRAGMENT_REGISTRY_VERSION,
    fragment,
  });
}

function quarantineEvidenceHash(evidence) {
  return sha256({
    type: 'mazex.fragment.quarantine.evidence',
    version: FRAGMENT_REGISTRY_VERSION,
    evidence,
  });
}

function makeQuarantineProof(input = {}, { currentHeight = 0, createdAt = null } = {}) {
  const check = validateQuarantineInput(input);
  if (!check.ok) {
    return {
      ok: false,
      code: 'FRAGMENT_QUARANTINE_INVALID',
      errors: check.errors,
    };
  }
  const quarantine = check.quarantine;
  const proof = {
    type: 'mazex.fragment.quarantine',
    registry_version: FRAGMENT_REGISTRY_VERSION,
    fragment_id: quarantine.fragment_id,
    challenger: quarantine.challenger,
    reason_code: quarantine.reason_code,
    evidence_hash: quarantineEvidenceHash(quarantine.evidence),
    evidence_bytes: check.evidence_bytes,
    evidence: quarantine.evidence,
    quarantined_at_height: Math.max(0, Math.trunc(Number(currentHeight || 0))),
    created_at: createdAt || quarantine.created_at || null,
  };
  return {
    ok: true,
    quarantine: {
      ...proof,
      proof_hash: sha256(proof),
    },
  };
}

function makeFragmentRecord(input = {}, { currentHeight = 0, activationDelay = 0, createdAt = null } = {}) {
  const check = validateFragmentInput(input);
  if (!check.ok) {
    return {
      ok: false,
      code: 'FRAGMENT_INVALID',
      errors: check.errors,
    };
  }
  const fragment = check.fragment;
  const contentHash = fragmentContentHash(fragment);
  const activationHeight = Math.max(0, Math.trunc(Number(currentHeight || 0))) + Math.max(0, Math.trunc(Number(activationDelay || 0)));
  const recordPayload = {
    type: 'mazex.fragment.record',
    registry_version: FRAGMENT_REGISTRY_VERSION,
    kind: fragment.kind,
    name: fragment.name,
    version: fragment.version,
    author: fragment.author,
    content_hash: contentHash,
    behavior_fingerprint: check.behavior_fingerprint,
    payload_bytes: check.payload_bytes,
    budget: fragment.budget,
    compatibility: fragment.compatibility,
    supersedes_fragment_id: fragment.supersedes_fragment_id || null,
    deterministic: fragment.deterministic,
    activation_height: activationHeight,
    payload: fragment.payload,
  };
  const fragmentId = sha256({
    type: 'mazex.fragment.id',
    registry_version: FRAGMENT_REGISTRY_VERSION,
    kind: fragment.kind,
    name: fragment.name,
    version: fragment.version,
    author: fragment.author,
    content_hash: contentHash,
  });
  return {
    ok: true,
    record: {
      ...recordPayload,
      fragment_id: fragmentId,
      created_at: createdAt,
    },
  };
}

function fragmentLeaf(record) {
  const leaf = {
    type: 'mazex.fragment.leaf',
    registry_version: FRAGMENT_REGISTRY_VERSION,
    fragment_id: record.fragment_id,
    kind: record.kind,
    name: record.name,
    version: record.version,
    author: record.author,
    content_hash: record.content_hash,
    behavior_fingerprint: record.behavior_fingerprint || null,
    payload_bytes: record.payload_bytes,
    activation_height: record.activation_height,
    quarantined: Boolean(record.quarantined),
    retired: Boolean(record.retired),
  };
  if (record.retired) {
    leaf.retired_at_height = record.retired_at_height ?? null;
    leaf.retired_by_fragment_id = record.retired_by_fragment_id || null;
  }
  if (record.quarantined) {
    leaf.quarantine_proof_hash = record.quarantine?.proof_hash || null;
    leaf.quarantine_reason_code = record.quarantine?.reason_code || null;
    leaf.quarantined_at_height = record.quarantine?.quarantined_at_height ?? null;
  }
  return sha256(leaf);
}

function activeAt(record, height) {
  return !record.quarantined && !record.retired && Number(record.activation_height || 0) <= Math.max(0, Number(height || 0));
}

function normalizeRegistry(registry = {}) {
  const source = registry && typeof registry === 'object' ? registry : {};
  return {
    version: source.version || FRAGMENT_REGISTRY_VERSION,
    records: Array.isArray(source.records) ? source.records.map((record) => cloneJson(record)) : [],
  };
}

function computeFragmentRegistryRoots(registry = {}, { currentHeight = 0 } = {}) {
  const normalized = normalizeRegistry(registry);
  const sorted = normalized.records
    .slice()
    .sort((a, b) => String(a.fragment_id).localeCompare(String(b.fragment_id)));
  const allLeaves = sorted.map(fragmentLeaf);
  const activeRecords = sorted.filter((record) => activeAt(record, currentHeight));
  const activeLeaves = activeRecords.map(fragmentLeaf);
  const pendingCount = sorted.filter((record) => !record.quarantined && !activeAt(record, currentHeight)).length;
  const quarantinedCount = sorted.filter((record) => Boolean(record.quarantined)).length;
  const retiredCount = sorted.filter((record) => Boolean(record.retired)).length;
  return {
    version: normalized.version,
    current_height: Math.max(0, Math.trunc(Number(currentHeight || 0))),
    registry_root: merkleRoot(allLeaves),
    active_root: merkleRoot(activeLeaves),
    total_count: sorted.length,
    active_count: activeRecords.length,
    pending_count: pendingCount,
    quarantined_count: quarantinedCount,
    retired_count: retiredCount,
  };
}

function createDefaultFragmentRegistry() {
  const registry = normalizeRegistry();
  for (const item of DEFAULT_FRAGMENTS) {
    const made = makeFragmentRecord(
      {
        ...item,
        author: 'genesis',
        version: '1.0.0',
        budget: {
          min_layer: 1,
          score: 1,
          max_payload_bytes: MAX_FRAGMENT_PAYLOAD_BYTES,
        },
      },
      { currentHeight: 0, activationDelay: 0 },
    );
    if (made.ok) registry.records.push(made.record);
  }
  return {
    ...registry,
    ...computeFragmentRegistryRoots(registry, { currentHeight: 0 }),
  };
}

function addFragmentRecord(registry = {}, record = {}, { currentHeight = 0 } = {}) {
  const normalized = normalizeRegistry(registry);
  if (!record || typeof record !== 'object') {
    return { ok: false, code: 'FRAGMENT_RECORD_REQUIRED', message: 'Fragment record is required' };
  }
  const made = makeFragmentRecord(
    {
      kind: record.kind,
      name: record.name,
      version: record.version,
      author: record.author,
      payload: record.payload,
      budget: record.budget,
      deterministic: record.deterministic,
      compatibility: record.compatibility,
      supersedes_fragment_id: record.supersedes_fragment_id || null,
    },
    {
      currentHeight: Number(record.activation_height || currentHeight),
      activationDelay: 0,
      createdAt: record.created_at || null,
    },
  );
  if (!made.ok) return made;
  if (made.record.fragment_id !== record.fragment_id || made.record.content_hash !== record.content_hash) {
    return {
      ok: false,
      code: 'FRAGMENT_RECORD_HASH_MISMATCH',
      message: 'Fragment record hash cannot be reproduced',
    };
  }
  if (normalized.records.some((item) => item.fragment_id === record.fragment_id)) {
    return {
      ok: false,
      code: 'FRAGMENT_DUPLICATE',
      message: 'Fragment already exists in registry',
    };
  }
  const behaviorFingerprint = record.behavior_fingerprint || made.record.behavior_fingerprint;
  if (normalized.records.some((item) => item.content_hash === record.content_hash)) {
    return {
      ok: false,
      code: 'FRAGMENT_DUPLICATE_CONTENT',
      message: 'Fragment content already exists in registry',
    };
  }
  if (
    normalized.records.some(
      (item) =>
        item.kind === record.kind &&
        item.name === record.name &&
        item.version === record.version &&
        item.fragment_id !== record.fragment_id,
    )
  ) {
    return {
      ok: false,
      code: 'FRAGMENT_VERSION_CONFLICT',
      message: 'Fragment kind/name/version already exists with different content',
    };
  }
  if (
    behaviorFingerprint &&
    normalized.records.some(
      (item) =>
        !item.quarantined &&
        !item.retired &&
        item.kind === record.kind &&
        item.behavior_fingerprint === behaviorFingerprint &&
        item.fragment_id !== record.fragment_id,
    )
  ) {
    return {
      ok: false,
      code: 'FRAGMENT_DUPLICATE_BEHAVIOR',
      message: 'Fragment behavior fingerprint already exists in active registry history',
    };
  }
  normalized.records.push({ ...cloneJson(record), behavior_fingerprint: behaviorFingerprint });
  return {
    ok: true,
    registry: {
      ...normalized,
      ...computeFragmentRegistryRoots(normalized, { currentHeight }),
    },
    record: cloneJson(record),
  };
}

function registerFragment(registry = {}, input = {}, { currentHeight = 0, activationDelay = 10, createdAt = null } = {}) {
  const made = makeFragmentRecord(input, { currentHeight, activationDelay, createdAt });
  if (!made.ok) return made;
  return addFragmentRecord(registry, made.record, { currentHeight });
}

function upgradeFragment(
  registry = {},
  input = {},
  { previousFragmentId = '', currentHeight = 0, activationDelay = 10, createdAt = null } = {},
) {
  const normalized = normalizeRegistry(registry);
  const previousIndex = normalized.records.findIndex((record) => record.fragment_id === previousFragmentId);
  if (previousIndex < 0) {
    return { ok: false, code: 'FRAGMENT_PREVIOUS_NOT_FOUND', message: 'Previous fragment does not exist' };
  }
  const previous = normalized.records[previousIndex];
  if (previous.quarantined) return { ok: false, code: 'FRAGMENT_PREVIOUS_QUARANTINED', message: 'Previous fragment is quarantined' };
  if (previous.retired) return { ok: false, code: 'FRAGMENT_PREVIOUS_RETIRED', message: 'Previous fragment is already retired' };
  const made = makeFragmentRecord(
    {
      ...input,
      kind: input.kind || previous.kind,
      name: input.name || previous.name,
      author: input.author || input.owner || previous.author,
      supersedes_fragment_id: previous.fragment_id,
    },
    { currentHeight, activationDelay, createdAt },
  );
  if (!made.ok) return made;
  if (made.record.kind !== previous.kind || made.record.name !== previous.name) {
    return {
      ok: false,
      code: 'FRAGMENT_UPGRADE_IDENTITY_MISMATCH',
      message: 'Fragment upgrade must keep the same kind and name',
    };
  }
  if (made.record.version === previous.version) {
    return { ok: false, code: 'FRAGMENT_UPGRADE_VERSION_UNCHANGED', message: 'Fragment upgrade requires a new version' };
  }
  const retiredPrevious = {
    ...cloneJson(previous),
    retired: true,
    retired_at_height: Math.max(0, Math.trunc(Number(currentHeight || 0))),
    retired_by_fragment_id: made.record.fragment_id,
  };
  normalized.records[previousIndex] = retiredPrevious;
  const add = addFragmentRecord(normalized, made.record, { currentHeight });
  if (!add.ok) return add;
  return {
    ok: true,
    registry: add.registry,
    previous: retiredPrevious,
    record: made.record,
  };
}

function quarantineFragment(registry = {}, input = {}, { currentHeight = 0, createdAt = null } = {}) {
  const normalized = normalizeRegistry(registry);
  const proof = makeQuarantineProof(input, { currentHeight, createdAt });
  if (!proof.ok) return proof;
  const index = normalized.records.findIndex((record) => record.fragment_id === proof.quarantine.fragment_id);
  if (index < 0) {
    return {
      ok: false,
      code: 'FRAGMENT_NOT_FOUND',
      message: 'Fragment does not exist in registry',
    };
  }
  if (normalized.records[index].quarantined) {
    return {
      ok: false,
      code: 'FRAGMENT_ALREADY_QUARANTINED',
      message: 'Fragment is already quarantined',
    };
  }
  const record = {
    ...cloneJson(normalized.records[index]),
    quarantined: true,
    quarantine: proof.quarantine,
  };
  normalized.records[index] = record;
  return {
    ok: true,
    registry: {
      ...normalized,
      ...computeFragmentRegistryRoots(normalized, { currentHeight }),
    },
    record: cloneJson(record),
    quarantine: cloneJson(proof.quarantine),
  };
}

function publicFragmentRegistry(registry = {}, { currentHeight = 0, limit = 50 } = {}) {
  const normalized = normalizeRegistry(registry);
  const roots = computeFragmentRegistryRoots(normalized, { currentHeight });
  const records = normalized.records
    .slice()
    .sort((a, b) => String(a.fragment_id).localeCompare(String(b.fragment_id)))
    .slice(0, Math.max(0, Math.trunc(Number(limit || 50))))
    .map((record) => ({
      fragment_id: record.fragment_id,
      kind: record.kind,
      name: record.name,
      version: record.version,
      author: record.author,
      content_hash: record.content_hash,
      payload_bytes: record.payload_bytes,
      activation_height: record.activation_height,
      supersedes_fragment_id: record.supersedes_fragment_id || null,
      behavior_fingerprint: record.behavior_fingerprint || null,
      status: activeAt(record, currentHeight)
        ? 'active'
        : record.quarantined
          ? 'quarantined'
          : record.retired
            ? 'retired'
            : 'pending',
      retired_by_fragment_id: record.retired_by_fragment_id || null,
      quarantine: record.quarantined
        ? {
            proof_hash: record.quarantine?.proof_hash || null,
            reason_code: record.quarantine?.reason_code || null,
            challenger: record.quarantine?.challenger || null,
            quarantined_at_height: record.quarantine?.quarantined_at_height ?? null,
          }
        : null,
    }));
  return {
    ...roots,
    records,
  };
}

function fragmentRecordPackage(registry = {}, { fragmentId = '', currentHeight = 0 } = {}) {
  const normalized = normalizeRegistry(registry);
  const roots = computeFragmentRegistryRoots(normalized, { currentHeight });
  const sorted = normalized.records.slice().sort((a, b) => String(a.fragment_id).localeCompare(String(b.fragment_id)));
  const index = sorted.findIndex((record) => record.fragment_id === fragmentId);
  if (index < 0) return { ok: false, code: 'FRAGMENT_NOT_FOUND', message: 'Fragment does not exist in registry' };
  const leaves = sorted.map(fragmentLeaf);
  return {
    ok: true,
    registry_version: roots.version,
    current_height: roots.current_height,
    fragment: cloneJson(sorted[index]),
    leaf_hash: leaves[index],
    merkle_proof: merkleProof(leaves, index),
    registry_root: roots.registry_root,
    active_root: roots.active_root,
    status: activeAt(sorted[index], currentHeight)
      ? 'active'
      : sorted[index].quarantined
        ? 'quarantined'
        : sorted[index].retired
          ? 'retired'
          : 'pending',
  };
}

function fragmentDownloadPackage(registry = {}, { currentHeight = 0, limit = 1000, includePayload = true } = {}) {
  const normalized = normalizeRegistry(registry);
  const roots = computeFragmentRegistryRoots(normalized, { currentHeight });
  const safeLimit = Math.min(MAX_FRAGMENT_DOWNLOAD_LIMIT, Math.max(0, Math.trunc(Number(limit || MAX_FRAGMENT_DOWNLOAD_LIMIT))));
  const sorted = normalized.records.slice().sort((a, b) => String(a.fragment_id).localeCompare(String(b.fragment_id)));
  const records = sorted.slice(0, safeLimit).map((record) => {
    const output = cloneJson(record);
    if (!includePayload) delete output.payload;
    return output;
  });
  return {
    ok: true,
    ...roots,
    limit: safeLimit,
    returned_count: records.length,
    complete: records.length === sorted.length,
    records,
    download_hash: sha256({
      type: 'mazex.fragment.download',
      registry_version: roots.version,
      registry_root: roots.registry_root,
      active_root: roots.active_root,
      records,
    }),
  };
}

function selectFragmentsForStep(registry = {}, { currentHeight = 0, step = {}, seed = '', maxCount = 8 } = {}) {
  const normalized = normalizeRegistry(registry);
  const roots = computeFragmentRegistryRoots(normalized, { currentHeight });
  const active = normalized.records
    .filter((record) => activeAt(record, currentHeight))
    .sort((a, b) => String(a.fragment_id).localeCompare(String(b.fragment_id)));
  const safeCount = Math.min(active.length, Math.max(0, Math.trunc(Number(maxCount || 8))));
  const ranked = active
    .map((record) => ({
      record,
      rank_hash: sha256({
        type: 'mazex.fragment.selection.rank',
        registry_version: FRAGMENT_REGISTRY_VERSION,
        seed,
        layer_no: step.layer_no,
        step_no: step.step_no,
        fragment_id: record.fragment_id,
        active_root: roots.active_root,
      }),
    }))
    .sort((a, b) => a.rank_hash.localeCompare(b.rank_hash) || a.record.fragment_id.localeCompare(b.record.fragment_id))
    .slice(0, safeCount);
  const selected = ranked.map(({ record, rank_hash }, index) => ({
    index,
    fragment_id: record.fragment_id,
    kind: record.kind,
    name: record.name,
    version: record.version,
    content_hash: record.content_hash,
    behavior_fingerprint: record.behavior_fingerprint || null,
    budget_score: Math.max(1, Math.trunc(Number(record.budget?.score || 1))),
    min_layer: Math.max(1, Math.trunc(Number(record.budget?.min_layer || 1))),
    payload_profile_hash: sha256({
      type: 'mazex.fragment.payload_profile',
      registry_version: FRAGMENT_REGISTRY_VERSION,
      kind: record.kind,
      name: record.name,
      version: record.version,
      content_hash: record.content_hash,
      behavior_fingerprint: record.behavior_fingerprint || null,
      payload: record.payload || {},
      budget: record.budget || {},
    }),
    rank_hash,
  }));
  const selectionRoot = merkleRoot(
    selected.map((item) =>
      sha256({
        type: 'mazex.fragment.selection.leaf',
        registry_version: FRAGMENT_REGISTRY_VERSION,
        ...item,
      }),
    ),
  );
  const compositionPlan = {
    type: 'mazex.fragment.composition.plan',
    registry_version: FRAGMENT_REGISTRY_VERSION,
    layer_no: step.layer_no,
    step_no: step.step_no,
    active_root: roots.active_root,
    selection_root: selectionRoot,
    selected_count: selected.length,
    selected_kinds: selected.reduce((acc, item) => {
      acc[item.kind] = (acc[item.kind] || 0) + 1;
      return acc;
    }, {}),
    selected_score_sum: selected.reduce((sum, item) => sum + Math.max(1, Number(item.budget_score || 1)), 0),
  };
  const kindCounts = compositionPlan.selected_kinds;
  const scoreSum = compositionPlan.selected_score_sum;
  const generationProfilePayload = {
    type: 'mazex.fragment.generation_profile',
    registry_version: FRAGMENT_REGISTRY_VERSION,
    layer_no: step.layer_no,
    step_no: step.step_no,
    active_root: roots.active_root,
    selection_root: selectionRoot,
    selected_count: selected.length,
    kind_counts: kindCounts,
    score_sum: scoreSum,
    selected_payload_profiles: selected.map((item) => ({
      fragment_id: item.fragment_id,
      kind: item.kind,
      budget_score: item.budget_score,
      payload_profile_hash: item.payload_profile_hash,
    })),
  };
  return {
    ok: true,
    registry_root: roots.registry_root,
    active_root: roots.active_root,
    selection_root: selectionRoot,
    selection_hash: sha256({
      type: 'mazex.fragment.selection',
      registry_version: FRAGMENT_REGISTRY_VERSION,
      seed,
      step: {
        layer_no: step.layer_no,
        step_no: step.step_no,
        grid_size: step.grid_size,
      },
      selected,
    }),
    composition_plan_hash: sha256(compositionPlan),
    generation_profile: {
      ...generationProfilePayload,
      profile_hash: sha256(generationProfilePayload),
    },
    selected_count: selected.length,
    selected,
  };
}

module.exports = {
  FRAGMENT_REGISTRY_VERSION,
  MAX_FRAGMENT_PAYLOAD_BYTES,
  MAX_QUARANTINE_EVIDENCE_BYTES,
  QUARANTINE_REASON_CODES,
  createDefaultFragmentRegistry,
  computeFragmentRegistryRoots,
  registerFragment,
  upgradeFragment,
  quarantineFragment,
  addFragmentRecord,
  fragmentDownloadPackage,
  fragmentRecordPackage,
  fragmentBehaviorFingerprint,
  selectFragmentsForStep,
  publicFragmentRegistry,
  validateFragmentInput,
  validateQuarantineInput,
};
