// src/engine/reflection/silent-fork.ts
//
// Silently forks a built-in piece YAML into a user-specific copy under
// data/users/{userId}/pieces/{pieceName}.yaml.  Stamps the copy with
// frontmatter fields:
//
//   forked_from_commit: <git SHA of the source at fork time, or 'unknown'>
//   forked_at:          <ISO-8601 timestamp>
//
// These fields let Phase 7.4 detect upstream drift (built-in changed
// since the user's fork was taken).
//
// Returns { forked: false, commit: null } when the custom version already
// exists — guarantees idempotency without overwriting user edits.
// Throws when the built-in source file does not exist.
// Survives environments without a .git/ checkout (commit is null, copy
// still happens).

import { execSync } from 'child_process';
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
import { join, dirname } from 'path';
import matter from 'gray-matter';
import { userPiecesDir } from '../../user-folder/paths.js';

/**
 * dataDir: the per-user folder root (typically `data/users`). Aligned with the
 * rest of the codebase — userPiecesDir(dataDir, userId) resolves to
 * `{dataDir}/{userId}/pieces`. Earlier drafts required dataDir to be the parent
 * of `users/` and applied an extra `'users/'` segment internally; that was
 * inconsistent and forced callers to bridge with helpers like
 * piece-writer's old toSilentForkDataDir().
 */
export function silentFork(
  builtinDir: string,
  dataDir: string,
  userId: string,
  pieceName: string
): { forked: boolean; commit: string | null } {
  const srcPath = join(builtinDir, `${pieceName}.yaml`);
  const dstPath = join(userPiecesDir(dataDir, userId), `${pieceName}.yaml`);

  // No-op when the custom version already exists.
  if (existsSync(dstPath)) return { forked: false, commit: null };

  // Throw if the built-in source doesn't exist.
  if (!existsSync(srcPath)) {
    throw new Error(`built-in piece not found: ${pieceName}`);
  }

  const raw = readFileSync(srcPath, 'utf-8');

  // Capture the git commit SHA of the source file.
  // Falls back gracefully if git is unavailable or the file isn't tracked.
  let commit: string | null = null;
  try {
    const result = execSync(
      `git log -1 --format=%H -- ${JSON.stringify(srcPath)}`,
      { encoding: 'utf-8' }
    ).trim();
    commit = result || null;
  } catch {
    // Not a git checkout, or git not installed — proceed without SHA.
  }

  // Parse existing frontmatter (if any) and stamp fork metadata.
  const parsed = matter(raw);
  parsed.data.forked_from_commit = commit ?? 'unknown';
  parsed.data.forked_at = new Date().toISOString();
  const out = matter.stringify(parsed.content, parsed.data);

  // Write to user dir, creating directories as needed.
  mkdirSync(dirname(dstPath), { recursive: true });
  writeFileSync(dstPath, out);

  return { forked: true, commit };
}
