import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import {
  mkdtempSync,
  mkdirSync,
  writeFileSync,
  readFileSync,
  existsSync,
  chmodSync,
  rmSync,
} from 'node:fs';
import { join } from 'node:path';
import { tmpdir } from 'node:os';
import { syncDirectory } from '../../src/platform/file-ops.js';

describe('syncDirectory', () => {
  let tmpDir: string;

  beforeEach(() => {
    tmpDir = mkdtempSync(join(tmpdir(), 'mosaic-file-ops-'));
  });

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

  it('is a no-op when source and target are the same path', () => {
    const dir = join(tmpDir, 'same');
    mkdirSync(dir, { recursive: true });
    writeFileSync(join(dir, 'file.txt'), 'hello');
    // Should not throw even with read-only files
    const gitDir = join(dir, '.git', 'objects', 'pack');
    mkdirSync(gitDir, { recursive: true });
    const packFile = join(gitDir, 'pack-abc.idx');
    writeFileSync(packFile, 'data');
    chmodSync(packFile, 0o444);

    expect(() => syncDirectory(dir, dir)).not.toThrow();
  });

  it('skips nested .git directories when excludeGit is true', () => {
    const src = join(tmpDir, 'src');
    const dest = join(tmpDir, 'dest');

    // Create source with a nested .git
    mkdirSync(join(src, 'sources', 'skills', '.git', 'objects'), { recursive: true });
    writeFileSync(join(src, 'sources', 'skills', '.git', 'objects', 'pack.idx'), 'git-data');
    writeFileSync(join(src, 'sources', 'skills', 'SKILL.md'), 'skill content');
    writeFileSync(join(src, 'README.md'), 'readme');

    syncDirectory(src, dest, { excludeGit: true });

    // .git contents should NOT be copied
    expect(existsSync(join(dest, 'sources', 'skills', '.git'))).toBe(false);
    // Normal files should be copied
    expect(readFileSync(join(dest, 'sources', 'skills', 'SKILL.md'), 'utf-8')).toBe(
      'skill content',
    );
    expect(readFileSync(join(dest, 'README.md'), 'utf-8')).toBe('readme');
  });

  it('copies nested .git directories when excludeGit is false', () => {
    const src = join(tmpDir, 'src');
    const dest = join(tmpDir, 'dest');

    mkdirSync(join(src, 'sub', '.git'), { recursive: true });
    writeFileSync(join(src, 'sub', '.git', 'HEAD'), 'ref: refs/heads/main');

    syncDirectory(src, dest, { excludeGit: false });

    expect(readFileSync(join(dest, 'sub', '.git', 'HEAD'), 'utf-8')).toBe('ref: refs/heads/main');
  });

  it('respects preserve option', () => {
    const src = join(tmpDir, 'src');
    const dest = join(tmpDir, 'dest');

    mkdirSync(src, { recursive: true });
    mkdirSync(dest, { recursive: true });
    writeFileSync(join(src, 'SOUL.md'), 'new soul');
    writeFileSync(join(dest, 'SOUL.md'), 'old soul');
    writeFileSync(join(src, 'README.md'), 'new readme');

    syncDirectory(src, dest, { preserve: ['SOUL.md'] });

    expect(readFileSync(join(dest, 'SOUL.md'), 'utf-8')).toBe('old soul');
    expect(readFileSync(join(dest, 'README.md'), 'utf-8')).toBe('new readme');
  });
});
