/**
 * Cassette Key Generation
 *
 * Builds stable SHA256-based identifiers for cassettes.
 * Two spans are separate concerns:
 * - hashWorktreeFiles: fingerprints the worktree state at spawn time (for execute mode drift)
 * - buildCassetteKey: hashes all key components into a 32-char hex filename
 */

import { createHash } from 'node:crypto';
import { readdirSync, readFileSync } from 'node:fs';
import { join } from 'node:path';
import type { CassetteKey } from './types.js';

/**
 * Recursively hash all non-hidden files in a directory.
 *
 * Hidden entries (starting with '.') are skipped — this excludes .git, .cw, etc.
 * Entries are processed in sorted order for determinism across platforms.
 *
 * Returns the first 16 hex chars of the SHA256, or 'empty' if the directory
 * is absent or contains no readable files.
 */
export function hashWorktreeFiles(dir: string): string {
  const hash = createHash('sha256');
  let hasContent = false;

  function walkDir(currentDir: string): void {
    let entries;
    try {
      entries = readdirSync(currentDir, { withFileTypes: true });
    } catch {
      return;
    }

    for (const entry of [...entries].sort((a, b) => a.name.localeCompare(b.name))) {
      if (entry.name.startsWith('.')) continue;

      const fullPath = join(currentDir, entry.name);
      const relPath = fullPath.slice(dir.length);

      if (entry.isDirectory()) {
        hash.update(`d:${relPath}\n`);
        walkDir(fullPath);
      } else if (entry.isFile()) {
        try {
          const content = readFileSync(fullPath);
          hash.update(`f:${relPath}:${content.length}\n`);
          hash.update(content);
          hasContent = true;
        } catch {
          // skip unreadable files
        }
      }
    }
  }

  walkDir(dir);
  return hasContent ? hash.digest('hex').slice(0, 16) : 'empty';
}

/**
 * Compute a stable 32-char hex identifier for a cassette key.
 *
 * modelArgs are sorted before hashing so insertion order differences
 * between providers don't produce different cassettes.
 */
export function buildCassetteKey(key: CassetteKey): string {
  const canonical = JSON.stringify({
    normalizedPrompt: key.normalizedPrompt,
    providerName: key.providerName,
    modelArgs: [...key.modelArgs].sort(),
    worktreeHash: key.worktreeHash,
  });
  return createHash('sha256').update(canonical).digest('hex').slice(0, 32);
}
