import { describe, it, expect } from 'vitest';
import { findFilePaths, splitPath, truncatePathLabel } from '../detect';

describe('findFilePaths', () => {
  it('detects the repro path embedded in Cyrillic prose', () => {
    const text =
      'прочти манифест /Users/markinmatrix/workspace/@projects/cmdop/@docs/positioning/manifest.md интересно твоё мнение';
    const matches = findFilePaths(text);
    expect(matches).toHaveLength(1);
    expect(matches[0]!.path).toBe(
      '/Users/markinmatrix/workspace/@projects/cmdop/@docs/positioning/manifest.md',
    );
    // The slice at [start,end) must equal the matched path exactly.
    expect(text.slice(matches[0]!.start, matches[0]!.end)).toBe(matches[0]!.path);
  });

  it('detects a Windows drive path with backslashes', () => {
    const matches = findFilePaths('see C:\\Users\\me\\notes.md please');
    expect(matches).toHaveLength(1);
    expect(matches[0]!.path).toBe('C:\\Users\\me\\notes.md');
  });

  it('detects a Windows drive path with forward slashes', () => {
    const matches = findFilePaths('open C:/Users/me/notes.md');
    expect(matches[0]!.path).toBe('C:/Users/me/notes.md');
  });

  it('detects a UNC path', () => {
    const matches = findFilePaths('on \\\\srv\\share\\x.md here');
    expect(matches).toHaveLength(1);
    expect(matches[0]!.path).toBe('\\\\srv\\share\\x.md');
  });

  it('detects a ~ home path', () => {
    const matches = findFilePaths('edit ~/dev/a.ts now');
    expect(matches).toHaveLength(1);
    expect(matches[0]!.path).toBe('~/dev/a.ts');
  });

  it('detects a file:// URI', () => {
    const matches = findFilePaths('go to file:///x/y.md ok');
    expect(matches).toHaveLength(1);
    expect(matches[0]!.path).toBe('file:///x/y.md');
  });

  it('detects a directory path ending in a folder', () => {
    const matches = findFilePaths('cd /Users/me/positioning');
    expect(matches).toHaveLength(1);
    expect(matches[0]!.path).toBe('/Users/me/positioning');
  });

  it('detects a directory path ending in a trailing separator', () => {
    const matches = findFilePaths('cd /Users/me/positioning/ then');
    expect(matches[0]!.path).toBe('/Users/me/positioning/');
  });

  it('trims trailing sentence punctuation', () => {
    const matches = findFilePaths('see /a/b/c.md. Done.');
    expect(matches).toHaveLength(1);
    expect(matches[0]!.path).toBe('/a/b/c.md');
  });

  it('trims a closing paren', () => {
    const matches = findFilePaths('(at /a/b/c.md)');
    expect(matches[0]!.path).toBe('/a/b/c.md');
  });

  // ── False positives ──

  it('does NOT match a prose slash "a / b"', () => {
    expect(findFilePaths('a / b')).toHaveLength(0);
  });

  it('does NOT match a single-segment root "/foo"', () => {
    expect(findFilePaths('just /foo here')).toHaveLength(0);
  });

  it('does NOT match an https URL as a file path', () => {
    expect(findFilePaths('visit https://x.com/y/z page')).toHaveLength(0);
  });

  it('does NOT match an http URL as a file path', () => {
    expect(findFilePaths('http://example.com/a/b')).toHaveLength(0);
  });

  it('does NOT match bare relative tokens', () => {
    expect(findFilePaths('run ./script and src/index.ts')).toHaveLength(0);
  });

  it('finds multiple paths in one string', () => {
    const matches = findFilePaths('a /x/y.ts and ~/z/w.md done');
    expect(matches.map((m) => m.path)).toEqual(['/x/y.ts', '~/z/w.md']);
  });
});

describe('splitPath', () => {
  it('splits a unix file path', () => {
    expect(splitPath('/Users/me/manifest.md')).toEqual({
      dir: 'Users/me',
      base: 'manifest.md',
      isDir: false,
    });
  });

  it('treats an extensionless basename as a dir', () => {
    const r = splitPath('/Users/me/positioning');
    expect(r.base).toBe('positioning');
    expect(r.isDir).toBe(true);
  });

  it('treats a trailing-separator path as a dir', () => {
    const r = splitPath('/Users/me/docs/');
    expect(r.base).toBe('docs');
    expect(r.isDir).toBe(true);
  });

  it('is separator-agnostic for Windows paths', () => {
    const r = splitPath('C:\\Users\\me\\notes.md');
    expect(r.base).toBe('notes.md');
    expect(r.isDir).toBe(false);
  });

  it('strips a file:// scheme', () => {
    expect(splitPath('file:///x/y.md').base).toBe('y.md');
  });

  it('treats a dotfile as a file', () => {
    expect(splitPath('/home/me/.gitignore').isDir).toBe(false);
  });
});

describe('truncatePathLabel', () => {
  it('keeps a short path unchanged', () => {
    expect(truncatePathLabel('/a/b.md')).toBe('/a/b.md');
  });

  it('middle-ellipsizes a long path but keeps the basename', () => {
    const label = truncatePathLabel(
      '/Users/markinmatrix/workspace/@projects/cmdop/@docs/positioning/manifest.md',
    );
    expect(label).toContain('manifest.md');
    expect(label).toContain('…');
    // Basename is never cut.
    expect(label.endsWith('manifest.md')).toBe(true);
  });

  it('respects a custom maxChars', () => {
    const label = truncatePathLabel('/very/long/path/to/some/file.md', {
      maxChars: 16,
    });
    expect(label.endsWith('file.md')).toBe(true);
    expect(label).toContain('…');
  });

  it('keeps the parent folder hint for a long path', () => {
    const label = truncatePathLabel(
      '/Users/markinmatrix/workspace/@projects/cmdop/@docs/positioning/manifest.md',
    );
    expect(label).toContain('positioning');
  });
});
