/**
 * Attach an externally-synced project folder as a live RHA workspace.
 *
 * A folder synced in 'full' mode carries the complete substrate — ledger,
 * spec, IR manifest, freeze hashes, git history — which is exactly what makes
 * it re-attachable: any later prompting session continues the same provenance
 * chain instead of starting a dead copy. Validation is strict because the
 * substrate is the contract: a folder that fails integrity is surfaced for
 * reconcile-or-fork, never silently adopted.
 *
 * Single-writer: `.supergit/lock` ({ pid, ts }) guards against two sessions
 * appending to one ledger. A lock whose pid is dead is stale and reclaimable.
 */
import fs from 'node:fs';
import path from 'node:path';
import type { FeatureSpec, IRManifest } from './types';
import { checkScaffoldingIntegrity } from './scaffoldGuard';

export interface ProjectValidation {
  ok: boolean;
  dir: string;
  spec?: FeatureSpec;
  manifest?: IRManifest | null;
  error?: string;
}

export function validateProjectFolder(folder: string): ProjectValidation {
  const dir = path.resolve(folder.trim());
  if (!dir || !fs.existsSync(dir) || !fs.statSync(dir).isDirectory()) {
    return { ok: false, dir, error: 'folder does not exist' };
  }
  if (!fs.existsSync(path.join(dir, '.supergit', 'ledger.csv'))) {
    return { ok: false, dir, error: 'not an RHA project — .supergit/ledger.csv missing' };
  }
  if (!fs.existsSync(path.join(dir, '.git'))) {
    return {
      ok: false, dir,
      error: 'git history missing — this looks like a "deployable" sync; only "full" syncs carry the substrate and are re-attachable',
    };
  }
  let spec: FeatureSpec;
  try {
    spec = JSON.parse(fs.readFileSync(path.join(dir, '.supergit', 'spec.json'), 'utf8')) as FeatureSpec;
    if (!Array.isArray(spec.features)) throw new Error('no features array');
  } catch (e) {
    return { ok: false, dir, error: `spec.json missing or unparseable: ${e instanceof Error ? e.message : String(e)}` };
  }
  let manifest: IRManifest | null = null;
  try {
    manifest = JSON.parse(fs.readFileSync(path.join(dir, '.supergit', 'ir-manifest.json'), 'utf8')) as IRManifest;
  } catch { /* pre-freeze project: attachable, no frozen contract yet */ }
  // U3: same integrity guard as resume / recompute / add-features.
  if (manifest) {
    const guard = checkScaffoldingIntegrity(dir, manifest);
    if (!guard.ok) {
      return {
        ok: false, dir,
        error: guard.message
          ?? 'scaffolding interrupted — external edits detected. Reconcile or fork rather than continuing silently.',
      };
    }
  }
  return { ok: true, dir, spec, manifest };
}

export interface LockResult {
  ok: boolean;
  /** Set when a dead session's lock was reclaimed. */
  warning?: string;
  error?: string;
}

const lockPath = (dir: string) => path.join(dir, '.supergit', 'lock');

function pidAlive(pid: number): boolean {
  try {
    process.kill(pid, 0);
    return true;
  } catch {
    return false;
  }
}

/** Acquire the single-writer lock, reclaiming stale (dead-pid) locks. */
export function acquireProjectLock(dir: string): LockResult {
  const p = lockPath(dir);
  let warning: string | undefined;
  if (fs.existsSync(p)) {
    try {
      const l = JSON.parse(fs.readFileSync(p, 'utf8')) as { pid?: number; ts?: string };
      if (l.pid && l.pid !== process.pid && pidAlive(l.pid)) {
        return { ok: false, error: `project is locked by another live session (pid ${l.pid}, since ${l.ts ?? 'unknown'})` };
      }
      if (l.pid !== process.pid) warning = `stale lock from dead pid ${l.pid ?? '?'} reclaimed`;
    } catch {
      warning = 'unreadable lock file replaced';
    }
  }
  fs.writeFileSync(p, JSON.stringify({ pid: process.pid, ts: new Date().toISOString() }) + '\n');
  return { ok: true, warning };
}

export function releaseProjectLock(dir: string): void {
  try {
    const p = lockPath(dir);
    if (!fs.existsSync(p)) return;
    const l = JSON.parse(fs.readFileSync(p, 'utf8')) as { pid?: number };
    if (l.pid === process.pid) fs.rmSync(p, { force: true });
  } catch { /* best-effort */ }
}
