#!/usr/bin/env node
'use strict';

const { describe, it, beforeEach, afterEach } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('fs');
const path = require('path');
const os = require('os');
const { spawnSync } = require('child_process');

const SCRIPT = path.join(__dirname, 'stamp-worktree.js');

let tmpDir;
let agentsDir;

beforeEach(() => {
  tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'stamp-worktree-test-'));
  agentsDir = path.join(tmpDir, 'agents');
});

afterEach(() => {
  fs.rmSync(tmpDir, { recursive: true, force: true });
});

function run(args, env = {}) {
  const baseEnv = { ...process.env };
  delete baseEnv.CLAUDE_PLUGIN_ROOT;
  delete baseEnv.PLUGIN_ROOT;
  delete baseEnv.CLAUDE_SESSION_ID;
  delete baseEnv.CLAUDE_CODE_SESSION_ID;
  return spawnSync('node', [SCRIPT, ...args], {
    env: { ...baseEnv, AGENT_DASHBOARD_DIR: tmpDir, ...env },
    encoding: 'utf8',
  });
}

function initGitRepo(dir, branch) {
  fs.mkdirSync(dir, { recursive: true });
  const init = spawnSync('git', ['init', '-b', branch], { cwd: dir, encoding: 'utf8' });
  assert.equal(init.status, 0, init.stderr);
}

describe('stamp-worktree.js', () => {
  it('writes worktree_cwd and branch when agent state is empty', () => {
    const sessionId = 'sess-1';
    const wt = path.join(tmpDir, 'wt');
    initGitRepo(wt, 'feat/pinned');
    const r = run([wt], { CLAUDE_SESSION_ID: sessionId });
    assert.equal(r.status, 0, r.stderr);
    const written = JSON.parse(fs.readFileSync(path.join(agentsDir, sessionId + '.json'), 'utf8'));
    assert.equal(written.worktree_cwd, wt);
    assert.equal(written.branch, 'feat/pinned',
      'stamp-worktree pins branch alongside worktree_cwd so the dashboard does not have to live-read');
  });

  it('preserves existing worktree_cwd (first-write-wins)', () => {
    const sessionId = 'sess-2';
    fs.mkdirSync(agentsDir, { recursive: true });
    fs.writeFileSync(path.join(agentsDir, sessionId + '.json'),
      JSON.stringify({ worktree_cwd: '/already/set' }));
    const r = run(['/some/other/path'], { CLAUDE_SESSION_ID: sessionId });
    assert.equal(r.status, 0, r.stderr);
    const written = JSON.parse(fs.readFileSync(path.join(agentsDir, sessionId + '.json'), 'utf8'));
    assert.equal(written.worktree_cwd, '/already/set');
  });

  it('fills missing branch from existing worktree_cwd', () => {
    const sessionId = 'sess-existing-cwd';
    const wt = path.join(tmpDir, 'existing-wt');
    initGitRepo(wt, 'feat/existing');
    fs.mkdirSync(agentsDir, { recursive: true });
    fs.writeFileSync(path.join(agentsDir, sessionId + '.json'),
      JSON.stringify({ worktree_cwd: wt }));

    const r = run(['/some/other/path'], { CLAUDE_SESSION_ID: sessionId });

    assert.equal(r.status, 0, r.stderr);
    const written = JSON.parse(fs.readFileSync(path.join(agentsDir, sessionId + '.json'), 'utf8'));
    assert.equal(written.worktree_cwd, wt);
    assert.equal(written.branch, 'feat/existing');
  });

  it('preserves existing branch (first-write-wins)', () => {
    const sessionId = 'sess-existing-branch';
    const wt = path.join(tmpDir, 'branch-wt');
    initGitRepo(wt, 'feat/new');
    fs.mkdirSync(agentsDir, { recursive: true });
    fs.writeFileSync(path.join(agentsDir, sessionId + '.json'),
      JSON.stringify({ worktree_cwd: wt, branch: 'feat/original' }));

    const r = run([wt], { CLAUDE_SESSION_ID: sessionId });

    assert.equal(r.status, 0, r.stderr);
    const written = JSON.parse(fs.readFileSync(path.join(agentsDir, sessionId + '.json'), 'utf8'));
    assert.equal(written.branch, 'feat/original',
      'an existing pinned branch must survive subsequent stamp-worktree calls');
  });

  it('exits 2 when worktree path arg is missing', () => {
    const r = run([], { CLAUDE_SESSION_ID: 'sess-3' });
    assert.equal(r.status, 2);
  });

  it('exits 2 when session id is missing (no env, no arg)', () => {
    const r = run(['/some/path']);
    assert.equal(r.status, 2);
  });

  it('resolves relative worktree paths to absolute', () => {
    const sessionId = 'sess-4';
    const r = run(['./foo'], { CLAUDE_SESSION_ID: sessionId });
    assert.equal(r.status, 0, r.stderr);
    const written = JSON.parse(fs.readFileSync(path.join(agentsDir, sessionId + '.json'), 'utf8'));
    assert.equal(written.worktree_cwd, path.resolve('./foo'));
  });

  it('accepts session id as positional arg when env unset', () => {
    const sessionId = 'sess-5';
    const r = run(['/tmp/x', sessionId]);
    assert.equal(r.status, 0, r.stderr);
    const written = JSON.parse(fs.readFileSync(path.join(agentsDir, sessionId + '.json'), 'utf8'));
    assert.equal(written.worktree_cwd, '/tmp/x');
  });

  it('reads CLAUDE_CODE_SESSION_ID env var (the name Claude Code actually exports)', () => {
    const sessionId = 'sess-6';
    const r = run(['/tmp/y'], { CLAUDE_CODE_SESSION_ID: sessionId });
    assert.equal(r.status, 0, r.stderr);
    const written = JSON.parse(fs.readFileSync(path.join(agentsDir, sessionId + '.json'), 'utf8'));
    assert.equal(written.worktree_cwd, '/tmp/y');
  });
});
