import type { Request } from 'express';
import type { Principal, Scope } from 'shared-auth';
import { readSessionPrincipal, scopesToAuditStrings } from './principal-extractor';

const PRINCIPAL: Principal = {
  user: {
    id: 'user-1',
    personId: 'person-1',
    entraOid: 'oid-1',
    tenantId: 'tenant-1',
    displayName: 'Alice',
  },
  privileges: ['Portal.Admin'],
  roles: ['rh'],
  scopes: [{ kind: 'unrestricted' }],
  amr: ['pwd', 'mfa'],
};

function makeReq(session: Record<string, unknown> | undefined): Request {
  return { session } as unknown as Request;
}

describe('readSessionPrincipal', () => {
  it('returns null when the request has no session at all', () => {
    expect(readSessionPrincipal(makeReq(undefined))).toBeNull();
  });

  it('returns null when the session has neither principal nor legacy user', () => {
    expect(readSessionPrincipal(makeReq({}))).toBeNull();
  });

  it('returns the principal verbatim when session.principal is set', () => {
    const out = readSessionPrincipal(makeReq({ principal: PRINCIPAL }));
    expect(out).toBe(PRINCIPAL);
  });

  describe('legacy bridge — session.user only (pre-ADR-0025 sessions)', () => {
    it('synthesises a principal carrying the Portal.* values as privileges', () => {
      const req = makeReq({
        user: {
          oid: 'legacy-oid',
          tid: 'tenant-7',
          username: 'legacy@example',
          displayName: 'Legacy Joe',
          amr: ['pwd', 'mfa'],
          roles: ['Portal.Admin', 'Portal.DPO', 'editor', 'auditor'],
          groups: [],
        },
      });
      const out = readSessionPrincipal(req);
      expect(out).not.toBeNull();
      expect(out!.privileges).toEqual(['Portal.Admin', 'Portal.DPO']);
      expect(out!.user.entraOid).toBe('legacy-oid');
      expect(out!.user.tenantId).toBe('tenant-7');
    });

    it('mirrors entraOid into user.id and user.personId (ADR-0026 placeholder)', () => {
      const req = makeReq({
        user: {
          oid: 'legacy-oid',
          tid: 'tenant-7',
          username: 'legacy@example',
          displayName: 'Legacy Joe',
          amr: [],
          roles: [],
          groups: [],
        },
      });
      const out = readSessionPrincipal(req);
      expect(out!.user.id).toBe('legacy-oid');
      expect(out!.user.personId).toBe('legacy-oid');
    });

    it('legacy sessions get no functional roles and a coarse unrestricted scope', () => {
      const req = makeReq({
        user: {
          oid: 'legacy-oid',
          tid: 'tenant-7',
          username: 'legacy@example',
          displayName: 'Legacy Joe',
          amr: [],
          roles: ['Portal.Admin'],
          groups: [],
        },
      });
      const out = readSessionPrincipal(req);
      expect(out!.roles).toEqual([]);
      expect(out!.scopes).toEqual([{ kind: 'unrestricted' }]);
    });
  });
});

describe('scopesToAuditStrings', () => {
  it('renders implicit scopes as the kind verbatim', () => {
    const scopes: Scope[] = [{ kind: 'self' }, { kind: 'siege' }, { kind: 'unrestricted' }];
    expect(scopesToAuditStrings(scopes)).toEqual(['self', 'siege', 'unrestricted']);
  });

  it('renders value-bearing scopes as kind:value', () => {
    const scopes: Scope[] = [
      { kind: 'etablissement', value: '0330800013' },
      { kind: 'delegation', value: '33' },
      { kind: 'region', value: '75' },
    ];
    expect(scopesToAuditStrings(scopes)).toEqual([
      'etablissement:0330800013',
      'delegation:33',
      'region:75',
    ]);
  });

  it('returns an empty array on an empty scope list', () => {
    expect(scopesToAuditStrings([])).toEqual([]);
  });
});
