const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const os = require('node:os');
const path = require('node:path');

const { captureClaudeMemory } = require('../lib/okf-capture');

function writeFile(filePath, content) {
  fs.mkdirSync(path.dirname(filePath), { recursive: true });
  fs.writeFileSync(filePath, content);
}

function readFile(filePath) {
  return fs.readFileSync(filePath, 'utf8');
}

function encodeRepoPath(repoRoot) {
  return path.resolve(repoRoot).replace(/[\\/]/g, '-');
}

function withFixture(testFn) {
  return async () => {
    const tmpdir = fs.mkdtempSync(path.join(os.tmpdir(), 'okf-capture-'));
    try {
      await testFn(tmpdir);
    } finally {
      fs.rmSync(tmpdir, { recursive: true, force: true });
    }
  };
}

test('captureClaudeMemory ingests claude-memory into project OKF concepts using fixture HOME only', withFixture((tmpdir) => {
  const homeDir = path.join(tmpdir, 'home');
  const repoRoot = path.join(tmpdir, 'repo');
  const encodedRepo = encodeRepoPath(repoRoot);
  const memoryFile = path.join(homeDir, '.claude', 'projects', encodedRepo, 'memory', 'project_megaplan_subprojects.md');

  writeFile(memoryFile, [
    '---',
    'metadata:',
    '  type: project',
    'name: project_megaplan_subprojects',
    'description: Tracks subprojects for the harness repo.',
    '---',
    '',
    'The harness has multiple subprojects that need coordinated planning.',
    '',
  ].join('\n'));

  const result = captureClaudeMemory({ homeDir, repoRoot });

  assert.equal(result.ingested.length, 1);
  assert.deepEqual(result.skipped, []);

  const conceptPath = path.join(repoRoot, '.okf', 'concepts', 'project_megaplan_subprojects.md');
  assert.deepEqual(result.ingested[0], {
    source: memoryFile,
    destination: conceptPath,
    scope: 'project',
    okfId: 'project_megaplan_subprojects',
  });

  const concept = readFile(conceptPath);
  assert.match(concept, /^---\ntype: project\ntitle: Project megaplan subprojects\ndescription: Tracks subprojects for the harness repo\.\nscope: project\nokf_id: project_megaplan_subprojects\nokf_source: claude-memory\norigin_filename: project_megaplan_subprojects\.md\norigin_hash: [a-f0-9]{64}\n---\n\nThe harness has multiple subprojects that need coordinated planning\.\n$/);
}));

test('captureClaudeMemory skips reruns when the OKF concept already matches the captured memory body', withFixture((tmpdir) => {
  const homeDir = path.join(tmpdir, 'home');
  const repoRoot = path.join(tmpdir, 'repo');
  const encodedRepo = encodeRepoPath(repoRoot);
  const memoryFile = path.join(homeDir, '.claude', 'projects', encodedRepo, 'memory', 'project_megaplan_subprojects.md');

  writeFile(memoryFile, [
    '---',
    'metadata:',
    '  type: project',
    'name: project_megaplan_subprojects',
    'description: Tracks subprojects for the harness repo.',
    '---',
    '',
    'The harness has multiple subprojects that need coordinated planning.',
    '',
  ].join('\n'));

  const first = captureClaudeMemory({ homeDir, repoRoot });
  const second = captureClaudeMemory({ homeDir, repoRoot });

  assert.equal(first.ingested.length, 1);
  assert.equal(second.ingested.length, 0);
  assert.equal(second.skipped.length, 1);
  assert.equal(second.skipped[0].reason, 'duplicate-body');
  assert.equal(second.skipped[0].source, memoryFile);
}));

test('captureClaudeMemory skips byte-identical OKF-projected memory files to avoid loops', withFixture((tmpdir) => {
  const homeDir = path.join(tmpdir, 'home');
  const repoRoot = path.join(tmpdir, 'repo');
  const encodedRepo = encodeRepoPath(repoRoot);
  const memoryFile = path.join(homeDir, '.claude', 'projects', encodedRepo, 'memory', 'fact-123.md');
  const conceptPath = path.join(repoRoot, '.okf', 'concepts', 'fact-123.md');

  writeFile(conceptPath, [
    '---',
    'type: reference',
    'title: Existing fact',
    'description: Existing captured fact.',
    'scope: project',
    'okf_id: fact-123',
    'okf_source: authored',
    'origin_filename: fact-123.md',
    'origin_hash: will-be-overwritten',
    '---',
    '',
    'This fact already exists in OKF.',
    '',
  ].join('\n'));

  writeFile(memoryFile, [
    '---',
    'type: reference',
    'title: Existing fact',
    'description: Existing captured fact.',
    'scope: project',
    'okf_id: fact-123',
    'okf_source: authored',
    'origin_filename: fact-123.md',
    'origin_hash: will-be-overwritten',
    '---',
    '',
    'This fact already exists in OKF.',
    '',
  ].join('\n'));

  const result = captureClaudeMemory({ homeDir, repoRoot });

  assert.deepEqual(result.ingested, []);
  assert.equal(result.skipped.length, 1);
  assert.equal(result.skipped[0].reason, 'okf-origin');
}));

test('captureClaudeMemory keeps the OKF master on conflict and writes a needs-merge sibling', withFixture((tmpdir) => {
  const homeDir = path.join(tmpdir, 'home');
  const repoRoot = path.join(tmpdir, 'repo');
  const encodedRepo = encodeRepoPath(repoRoot);
  const memoryFile = path.join(homeDir, '.claude', 'projects', encodedRepo, 'memory', 'fact-123.md');
  const conceptPath = path.join(repoRoot, '.okf', 'concepts', 'fact-123.md');

  writeFile(conceptPath, [
    '---',
    'type: reference',
    'title: Existing fact',
    'description: Existing captured fact.',
    'scope: project',
    'okf_id: fact-123',
    'okf_source: authored',
    'origin_filename: fact-123.md',
    'origin_hash: stale-hash',
    '---',
    '',
    'The OKF master copy stays authoritative.',
    '',
  ].join('\n'));

  writeFile(memoryFile, [
    '---',
    'metadata:',
    '  type: reference',
    'name: fact-123',
    'description: Existing captured fact.',
    '---',
    '',
    'An agent wrote a conflicting variant into native memory.',
    '',
  ].join('\n'));

  const result = captureClaudeMemory({ homeDir, repoRoot });
  const mergePath = path.join(repoRoot, '.okf', 'concepts', 'fact-123.needs-merge.md');

  assert.deepEqual(result.ingested, []);
  assert.equal(result.skipped.length, 1);
  assert.equal(result.skipped[0].reason, 'needs-merge');
  assert.equal(readFile(conceptPath).includes('The OKF master copy stays authoritative.'), true);
  assert.match(readFile(mergePath), /An agent wrote a conflicting variant into native memory\./);
}));
