/**
 * access-classify — pure-layer tests (derived `partial` label + subset verdicts).
 */
import { describe, it, expect } from 'vitest';
import {
  deriveAccessLabel,
  classifySubsetVisibility,
  classifyEmptyList,
  subsetVisibleForRole,
} from '../access-classify.js';
import type { AccessVerdict, Route, Subset } from '../plantest-schema.js';

function subset(visibleFor: string[]): Subset {
  return {
    id: 'btn',
    type: 'action',
    kind: 'button',
    action_kind: 'click',
    selector: 'button',
    is_destructive: false,
    visible_for: visibleFor,
    expect: 'ok',
  } as Subset;
}

function route(access: Record<string, AccessVerdict>, subsets: Subset[]): Route {
  return {
    id: 'R',
    route: '/r',
    navigation_strategy: 'menu_click',
    expect: { access_by_role: true, no_console_error: true },
    access,
    subsets,
  } as Route;
}

describe('deriveAccessLabel', () => {
  it('passes denied / redirect_login through unchanged', () => {
    expect(deriveAccessLabel(route({ viewer: 'denied' }, []), 'viewer')).toBe('denied');
    expect(deriveAccessLabel(route({ anonymous: 'redirect_login' }, []), 'anonymous')).toBe('redirect_login');
  });

  it('allowed with full subset visibility stays allowed', () => {
    const r = route({ admin: 'allowed' }, [subset(['admin']), subset(['admin'])]);
    expect(deriveAccessLabel(r, 'admin')).toBe('allowed');
  });

  it('allowed but a subset legitimately hidden becomes partial', () => {
    const r = route({ admin: 'allowed', manager: 'allowed' }, [subset(['admin'])]);
    expect(deriveAccessLabel(r, 'manager')).toBe('partial'); // manager not in visible_for
    expect(deriveAccessLabel(r, 'admin')).toBe('allowed');
  });

  it('a role missing from the access map defaults to denied', () => {
    expect(deriveAccessLabel(route({ admin: 'allowed' }, []), 'ghost')).toBe('denied');
  });
});

describe('classifySubsetVisibility', () => {
  const sub = subset(['admin']);

  it('role may see it: present => PASS RBAC-OK, absent => FAIL BUG-MISSING-ELEMENT', () => {
    expect(classifySubsetVisibility(sub, 'admin', 1)).toEqual({ pass: true, category: 'RBAC-OK' });
    expect(classifySubsetVisibility(sub, 'admin', 0)).toEqual({ pass: false, category: 'BUG-MISSING-ELEMENT' });
  });

  it('role may NOT see it: present => FAIL BUG-OVEREXPOSED, absent => PASS CONFIG-LEGITIME', () => {
    expect(classifySubsetVisibility(sub, 'viewer', 1)).toEqual({ pass: false, category: 'BUG-OVEREXPOSED' });
    expect(classifySubsetVisibility(sub, 'viewer', 0)).toEqual({ pass: true, category: 'CONFIG-LEGITIME' });
  });
});

describe('classifyEmptyList / subsetVisibleForRole', () => {
  it('empty parent list is INDETERMINATE, never denied', () => {
    expect(classifyEmptyList()).toEqual({ pass: true, category: 'INDETERMINATE' });
  });

  it('subsetVisibleForRole reflects visible_for membership', () => {
    expect(subsetVisibleForRole(subset(['admin']), 'admin')).toBe(true);
    expect(subsetVisibleForRole(subset(['admin']), 'viewer')).toBe(false);
  });
});
