import { describe, expect, it } from 'vitest';
import { buildChipMap, chipFor, folderMarkedStatus, rollupFolderStatus } from './statusChips';

function entry(path, status, detail = null) {
  return { path, status, detail };
}

describe('buildChipMap notAdded suppression during pulls', () => {
  it('flags a local-only file as notAdded when no pull is active', () => {
    const chips = buildChipMap([entry('New/thing.txt', 'notAdded')]);
    expect(chips.get('New/thing.txt')?.kind).toBe('notAdded');
  });

  it('suppresses notAdded for paths an in-flight pull is downloading', () => {
    const chips = buildChipMap(
      [entry('New/thing.txt', 'notAdded'), entry('Other/local.txt', 'notAdded')],
      { pullingPaths: new Set(['New/thing.txt']) }
    );
    // The pulled file gets no chip while it lands; a genuinely local file
    // still shows notAdded even mid-pull.
    expect(chips.has('New/thing.txt')).toBe(false);
    expect(chips.get('Other/local.txt')?.kind).toBe('notAdded');
  });

  it('never suppresses markedForAdd, even mid-pull', () => {
    const chips = buildChipMap([entry('Add/me.txt', 'markedForAdd')], {
      pullingPaths: new Set(['Add/me.txt']),
    });
    expect(chips.get('Add/me.txt')?.kind).toBe('markedForAdd');
  });

  it('never suppresses a naming issue, even mid-pull', () => {
    const chips = buildChipMap([entry('New/CON', 'namingIssue', { reason: 'reserved name' })], {
      pullingPaths: new Set(['New/CON']),
    });
    expect(chips.get('New/CON')?.kind).toBe('namingIssue');
  });
});

describe('buildChipMap serverReady suppression', () => {
  it('suppresses notAdded until the server tree is known', () => {
    const chips = buildChipMap([entry('New/thing.txt', 'notAdded')], { serverReady: false });
    expect(chips.get('New/thing.txt')).toBeUndefined();
  });

  it('shows notAdded when the server tree is known (default)', () => {
    const chips = buildChipMap([entry('New/thing.txt', 'notAdded')]);
    expect(chips.get('New/thing.txt')?.kind).toBe('notAdded');
  });
});

describe('chipFor composition from engine detail', () => {
  it('lockedByOther labels with the resolved holder display name', () => {
    const chip = chipFor(entry('a.txt', 'lockedByOther', { holder: 'dana', holderDisplayName: 'Dana Scully' }));
    expect(chip.kind).toBe('lockedByOther');
    expect(chip.label).toBe('Checked out by Dana Scully');
    expect(chip.color).toBe('orange');
  });

  it('lockedByMeElsewhere labels with the other device name', () => {
    const chip = chipFor(entry('a.txt', 'lockedByMeElsewhere', { deviceName: "Alice's Laptop" }));
    expect(chip.kind).toBe('lockedByMeElsewhere');
    expect(chip.label).toBe("Checked out by you on Alice's Laptop");
  });

  it('markedForDelete carries kind markedForDelete (FileTree strikethrough trigger)', () => {
    const chip = chipFor(entry('a.txt', 'markedForDelete'));
    expect(chip.kind).toBe('markedForDelete');
  });

  it('namingIssue title carries detail.reason via the explain template', () => {
    const chip = chipFor(entry('New/CON', 'namingIssue', { reason: 'CON is a reserved device name' }));
    expect(chip.kind).toBe('namingIssue');
    expect(chip.text).toContain('CON is a reserved device name');
  });

  it('an unrecognized status returns no chip', () => {
    expect(chipFor(entry('a.txt', 'somethingUnknown'))).toBeNull();
  });
});

describe('rollupFolderStatus', () => {
  const rootId = 'root::';
  const fileNode = (rel) => ({ type: 'file', id: `${rootId}${rel}` });

  it('returns null when no descendant file has a status', () => {
    const statuses = new Map();
    const node = { children: [fileNode('a.txt'), fileNode('b.txt')] };
    expect(rollupFolderStatus(node, statuses, rootId)).toBeNull();
  });

  it('returns the uniform status when every file shares one', () => {
    const statuses = new Map([
      ['a.txt', chipFor(entry('a.txt', 'markedForAdd'))],
      ['b.txt', chipFor(entry('b.txt', 'markedForAdd'))],
    ]);
    const node = { children: [fileNode('a.txt'), fileNode('b.txt')] };
    expect(rollupFolderStatus(node, statuses, rootId)?.kind).toBe('markedForAdd');
  });

  it('returns MIXED_FOLDER_STATUS when statuses differ', () => {
    const statuses = new Map([
      ['a.txt', chipFor(entry('a.txt', 'markedForAdd'))],
      ['b.txt', chipFor(entry('b.txt', 'modified'))],
    ]);
    const node = { children: [fileNode('a.txt'), fileNode('b.txt')] };
    expect(rollupFolderStatus(node, statuses, rootId)?.kind).toBe('mixed');
  });

  it('returns CONTENTS_MARKED_STATUS (not markedForDelete) when every file is individually marked', () => {
    const statuses = new Map([
      ['a.txt', chipFor(entry('a.txt', 'markedForDelete'))],
      ['b.txt', chipFor(entry('b.txt', 'markedForDelete'))],
    ]);
    const node = { children: [fileNode('a.txt'), fileNode('b.txt')] };
    expect(rollupFolderStatus(node, statuses, rootId)?.kind).toBe('contentsMarkedForDelete');
  });
});

describe('folderMarkedStatus', () => {
  it('carries kind markedForDelete distinct from CONTENTS_MARKED_STATUS', () => {
    expect(folderMarkedStatus().kind).toBe('markedForDelete');
  });
});
