const { buildPointContextSet, behaviorHashForStep } = require('./expression_tree_verifier');
const { merkleRoot, sha256 } = require('./hash');

const BEHAVIOR_COMMITMENT_VERSION = 'mazex.behavior_commitment.v1';

function behaviorLeaf({ step, pointContextSet, row, index }) {
  return sha256({
    type: 'mazex.behavior_leaf',
    version: BEHAVIOR_COMMITMENT_VERSION,
    algorithm: step.algorithm,
    layer_no: step.layer_no,
    step_no: step.step_no,
    grid_size: step.grid_size,
    point_mode: pointContextSet.mode,
    point_index: index,
    x: row.x,
    y: row.y,
    category: row.category,
    context_hash: row.context_hash,
    u: row.u,
    v: row.v,
  });
}

function behaviorCommitmentFromRows({ step, pointContextSet, behaviorHash, rows }) {
  const leaves = rows.map((row, index) => behaviorLeaf({ step, pointContextSet, row, index }));
  const root = merkleRoot(leaves);
  const metadata = {
    type: 'mazex.behavior_commitment',
    version: BEHAVIOR_COMMITMENT_VERSION,
    algorithm: step.algorithm,
    layer_no: step.layer_no,
    step_no: step.step_no,
    grid_size: step.grid_size,
    point_mode: pointContextSet.mode,
    point_count: pointContextSet.total,
    point_order: 'verifier_canonical_order',
    leaf_encoding: 'sha256(canonical_behavior_leaf_v1)',
    merkle_policy: 'duplicate_last_leaf_when_odd',
    point_set_hash: pointContextSet.point_set_hash,
    context_set_hash: pointContextSet.context_set_hash,
    behavior_hash: behaviorHash,
    behavior_root: root,
    leaf_count: leaves.length,
  };
  return {
    ...metadata,
    commitment_hash: sha256(metadata),
    leaves,
  };
}

function behaviorCommitmentForStep(step, options = {}) {
  const pointContextSet = buildPointContextSet(step, options);
  const behavior = behaviorHashForStep(step, pointContextSet);
  return behaviorCommitmentFromRows({
    step,
    pointContextSet,
    behaviorHash: behavior.behavior_hash,
    rows: behavior.rows,
  });
}

function publicBehaviorCommitment(commitment) {
  if (!commitment) return null;
  return {
    version: commitment.version,
    algorithm: commitment.algorithm,
    layer_no: commitment.layer_no,
    step_no: commitment.step_no,
    grid_size: commitment.grid_size,
    point_mode: commitment.point_mode,
    point_count: commitment.point_count,
    point_order: commitment.point_order,
    leaf_encoding: commitment.leaf_encoding,
    merkle_policy: commitment.merkle_policy,
    point_set_hash: commitment.point_set_hash,
    context_set_hash: commitment.context_set_hash,
    behavior_hash: commitment.behavior_hash,
    behavior_root: commitment.behavior_root,
    leaf_count: commitment.leaf_count,
    commitment_hash: commitment.commitment_hash,
  };
}

module.exports = {
  BEHAVIOR_COMMITMENT_VERSION,
  behaviorCommitmentForStep,
  publicBehaviorCommitment,
};
