import { promises as fsp } from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { afterEach, beforeEach, describe, expect, it } from 'vitest';

import {
  buildMemoryTree,
  memoryDir,
  readMemoryEntry,
  updateMemoryTreeNode,
  upsertMemoryEntry,
} from '../src/memory.js';

let dataDir = '';

beforeEach(async () => {
  dataDir = await fsp.mkdtemp(path.join(os.tmpdir(), 'od-memory-tree-'));
});

afterEach(async () => {
  await fsp.rm(dataDir, { recursive: true, force: true });
});

describe('memory tree helpers', () => {
  it('derives folder and entry nodes from the markdown memory store', async () => {
    await upsertMemoryEntry(
      dataDir,
      {
        name: 'Design agent goal',
        description: 'Open Design should evolve from accepted work',
        type: 'project',
        body: '- Keep design-system extraction in the loop',
      },
      { silent: true },
    );

    const tree = await buildMemoryTree(dataDir);

    expect(tree).toEqual(
      expect.arrayContaining([
        expect.objectContaining({
          id: 'folder:project',
          kind: 'folder',
          path: '/project',
          childrenCount: 1,
        }),
        expect.objectContaining({
          id: 'project_design_agent_goal',
          parentId: 'folder:project',
          kind: 'entry',
          path: '/project/project_design_agent_goal',
          scope: 'project',
        }),
      ]),
    );
  });

  it('edits and moves an entry through the tree-aware patch helper', async () => {
    const entry = await upsertMemoryEntry(
      dataDir,
      {
        name: 'Reusable pattern',
        description: 'Skill candidate',
        type: 'reference',
        body: '- Draft artifacts with provenance',
      },
      { silent: true },
    );

    const updated = await updateMemoryTreeNode(dataDir, entry.id, {
      name: 'Reusable pattern',
      description: 'Skill candidate promoted from automation',
      type: 'project',
      body: '- Draft artifacts with provenance\n- Promote repeatable work into skills',
    });

    expect(updated.type).toBe('project');
    expect(updated.description).toContain('promoted from automation');
    expect(updated.body).toContain('Promote repeatable work into skills');

    const stored = await readMemoryEntry(dataDir, entry.id);
    expect(stored?.type).toBe('project');

    const tree = await buildMemoryTree(dataDir);
    expect(tree).toEqual(
      expect.arrayContaining([
        expect.objectContaining({
          id: entry.id,
          parentId: 'folder:project',
          path: `/project/${entry.id}`,
        }),
      ]),
    );
  });

  it('rejects derived folder edits', async () => {
    await expect(
      updateMemoryTreeNode(dataDir, 'folder:project', { name: 'Project' }),
    ).rejects.toThrow('memory tree folders are derived');
  });

  it('returns an empty folder tree when the store is empty', async () => {
    await fsp.rm(memoryDir(dataDir), { recursive: true, force: true });
    const tree = await buildMemoryTree(dataDir);

    expect(tree.filter((node) => node.kind === 'folder')).toHaveLength(4);
    expect(tree.filter((node) => node.kind === 'entry')).toHaveLength(0);
  });
});
