// Tests for file conflict detection via mtime comparison.
// Uses bun:test. Tests the mtime-based conflict detection and atomic write logic
// from ui-electrobun/src/bun/handlers/files-handlers.ts and FileBrowser.svelte.

import { describe, it, expect } from 'bun:test';

// ── Replicated conflict detection logic ──────────────────────────────────────

interface FileStat {
  mtimeMs: number;
  size: number;
  error?: string;
}

/**
 * Check if the file was modified since we last read it.
 * Returns true if conflict detected (mtime differs).
 */
function hasConflict(readMtimeMs: number, currentStat: FileStat): boolean {
  if (readMtimeMs <= 0) return false;       // No baseline — skip check
  if (currentStat.error) return false;       // Can't stat — skip check
  return currentStat.mtimeMs > readMtimeMs;  // Modified since read
}

/**
 * Simulate atomic write: write to temp file, then rename.
 * Returns the operations performed for verification.
 */
function atomicWriteOps(filePath: string, _content: string): { tmpPath: string; finalPath: string } {
  const tmpPath = filePath + '.agor-tmp';
  return { tmpPath, finalPath: filePath };
}

// ── Tests ───────────────────────────────────────────────────────────────────

describe('mtime conflict detection', () => {
  it('no conflict when mtime matches', () => {
    const readTime = 1700000000000;
    const stat: FileStat = { mtimeMs: readTime, size: 100 };
    expect(hasConflict(readTime, stat)).toBe(false);
  });

  it('conflict detected when mtime is newer', () => {
    const readTime = 1700000000000;
    const stat: FileStat = { mtimeMs: readTime + 5000, size: 120 };
    expect(hasConflict(readTime, stat)).toBe(true);
  });

  it('no conflict when readMtimeMs is 0 (first write)', () => {
    const stat: FileStat = { mtimeMs: 1700000005000, size: 120 };
    expect(hasConflict(0, stat)).toBe(false);
  });

  it('no conflict when stat returns error', () => {
    const stat: FileStat = { mtimeMs: 0, size: 0, error: 'ENOENT: no such file' };
    expect(hasConflict(1700000000000, stat)).toBe(false);
  });

  it('no conflict when file is older than read (edge case)', () => {
    const readTime = 1700000005000;
    const stat: FileStat = { mtimeMs: 1700000000000, size: 100 };
    expect(hasConflict(readTime, stat)).toBe(false);
  });

  it('detects tiny mtime difference (1ms)', () => {
    const readTime = 1700000000000;
    const stat: FileStat = { mtimeMs: readTime + 1, size: 100 };
    expect(hasConflict(readTime, stat)).toBe(true);
  });
});

describe('atomic write', () => {
  it('uses .agor-tmp suffix for temp file', () => {
    const ops = atomicWriteOps('/home/user/project/main.ts', 'content');
    expect(ops.tmpPath).toBe('/home/user/project/main.ts.agor-tmp');
    expect(ops.finalPath).toBe('/home/user/project/main.ts');
  });

  it('temp file path differs from final path', () => {
    const ops = atomicWriteOps('/test/file.txt', 'data');
    expect(ops.tmpPath).not.toBe(ops.finalPath);
  });

  it('handles paths with special characters', () => {
    const ops = atomicWriteOps('/path/with spaces/file.ts', 'data');
    expect(ops.tmpPath).toBe('/path/with spaces/file.ts.agor-tmp');
  });
});

describe('conflict workflow', () => {
  it('full read-modify-check-write cycle — no conflict', () => {
    // 1. Read file, record mtime
    const readStat: FileStat = { mtimeMs: 1700000000000, size: 50 };
    const readMtimeMs = readStat.mtimeMs;

    // 2. User edits in editor
    // 3. Before save, stat again
    const preSaveStat: FileStat = { mtimeMs: 1700000000000, size: 50 }; // unchanged
    expect(hasConflict(readMtimeMs, preSaveStat)).toBe(false);

    // 4. Write via atomic
    const ops = atomicWriteOps('/test/file.ts', 'new content');
    expect(ops.tmpPath).toContain('.agor-tmp');
  });

  it('full read-modify-check-write cycle — conflict detected', () => {
    // 1. Read file, record mtime
    const readMtimeMs = 1700000000000;

    // 2. External process modifies the file
    const preSaveStat: FileStat = { mtimeMs: 1700000002000, size: 80 };

    // 3. Conflict detected — should warn user
    expect(hasConflict(readMtimeMs, preSaveStat)).toBe(true);
  });

  it('after successful save, update readMtimeMs', () => {
    let readMtimeMs = 1700000000000;

    // Save succeeds, stat again to get new mtime
    const postSaveStat: FileStat = { mtimeMs: 1700000003000, size: 120 };
    readMtimeMs = postSaveStat.mtimeMs;

    // No conflict on subsequent check
    expect(hasConflict(readMtimeMs, postSaveStat)).toBe(false);
  });
});
