// Copyright (c) 2026 defconxt. All rights reserved.
// Licensed under AGPL-3.0 — see LICENSE file for details.
// NERF is a trademark of defconxt.

/**
 * NERF Constitution Versioning
 *
 * Tracks CLAUDE.md changes via content hash. Provides version info
 * for audit trails and consistency checking.
 *
 * @module analyze/constitution
 */

import { readFileSync, existsSync } from 'node:fs';
import { createHash } from 'node:crypto';
import { join, resolve, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';

const __dirname = dirname(fileURLToPath(import.meta.url));

/**
 * Constitution version info.
 */
export class ConstitutionVersion {
  /**
   * @param {object} opts
   * @param {string} opts.hash        - SHA-256 hash of CLAUDE.md content
   * @param {string} opts.shortHash   - First 8 chars of hash
   * @param {number} opts.modes       - Number of modes defined
   * @param {string[]} opts.modeNames - Mode names
   * @param {number} opts.lines       - Total line count
   * @param {number} opts.size        - File size in bytes
   * @param {string} opts.path        - File path
   */
  constructor(opts = {}) {
    this.hash = opts.hash ?? '';
    this.shortHash = opts.shortHash ?? '';
    this.modes = opts.modes ?? 0;
    this.modeNames = opts.modeNames ?? [];
    this.lines = opts.lines ?? 0;
    this.size = opts.size ?? 0;
    this.path = opts.path ?? '';
  }

  toReport() {
    return [
      `Constitution: ${this.path}`,
      `Version hash: ${this.shortHash}`,
      `Modes: ${this.modeNames.join(', ')} (${this.modes})`,
      `Size: ${this.lines} lines, ${this.size} bytes`,
    ].join('\n');
  }

  toJSON() {
    return {
      hash: this.hash,
      shortHash: this.shortHash,
      modes: this.modes,
      modeNames: this.modeNames,
      lines: this.lines,
      size: this.size,
      path: this.path,
    };
  }
}

/**
 * Get constitution version info from CLAUDE.md.
 *
 * @param {string} [repoRoot] - Path to repo root (auto-detected if omitted)
 * @returns {ConstitutionVersion|null}
 */
export function getConstitutionVersion(repoRoot) {
  const root = repoRoot ?? findRoot();
  const claudePath = join(root, 'CLAUDE.md');

  if (!existsSync(claudePath)) return null;

  const content = readFileSync(claudePath, 'utf-8');
  const hash = createHash('sha256').update(content).digest('hex');

  // Extract modes
  const modeNames = [];
  const modeRe = /\[MODE:\s*([A-Z]+)\]/g;
  let match;
  while ((match = modeRe.exec(content)) !== null) {
    if (!modeNames.includes(match[1])) modeNames.push(match[1]);
  }

  return new ConstitutionVersion({
    hash,
    shortHash: hash.slice(0, 8),
    modes: modeNames.length,
    modeNames,
    lines: content.split('\n').length,
    size: Buffer.byteLength(content),
    path: claudePath,
  });
}

function findRoot() {
  let dir = resolve(__dirname, '..', '..');
  for (let i = 0; i < 10; i++) {
    if (existsSync(join(dir, 'CLAUDE.md'))) return dir;
    const parent = dirname(dir);
    if (parent === dir) break;
    dir = parent;
  }
  return process.cwd();
}
