/**
 * Authorization Service Tests
 */

import { describe, it, expect, beforeEach } from 'vitest';
import {
  CredentialAuthorizationService,
  getAuthorizationService,
  DEFAULT_ISSUANCE_RULES,
} from './authorization';
import type { AuthUser } from './auth';

describe('CredentialAuthorizationService', () => {
  let service: CredentialAuthorizationService;
  const testUser: AuthUser = {
    id: 'user-1',
    email: 'test@example.com',
    roles: ['admin'],
    did: 'did:web:example.com:user-1',
  };

  beforeEach(() => {
    service = new CredentialAuthorizationService();
  });

  describe('canIssueCredential', () => {
    it('should allow admin to issue identity credentials', async () => {
      const result = await service.canIssueCredential(
        testUser,
        ['VerifiableCredential', 'IdentityCredential']
      );

      expect(result.allowed).toBe(true);
      expect(result.requiresApproval).toBe(false);
    });

    it('should require approval for judicial credentials', async () => {
      const result = await service.canIssueCredential(
        testUser,
        ['VerifiableCredential', 'JudicialCredential', 'RegistrarCredential']
      );

      expect(result.allowed).toBe(true);
      expect(result.requiresApproval).toBe(true);
    });

    it('should deny access for unauthorized roles', async () => {
      const userWithoutRole: AuthUser = {
        ...testUser,
        roles: ['user'],
      };

      const result = await service.canIssueCredential(
        userWithoutRole,
        ['VerifiableCredential', 'JudicialCredential']
      );

      expect(result.allowed).toBe(false);
      expect(result.reason).toContain('required role');
    });

    it('should require approval for unknown credential types', async () => {
      const userWithoutAdmin: AuthUser = {
        ...testUser,
        roles: ['user'],
      };

      const result = await service.canIssueCredential(
        userWithoutAdmin,
        ['VerifiableCredential', 'UnknownCredential']
      );

      expect(result.allowed).toBe(false);
      expect(result.requiresApproval).toBe(true);
    });
  });

  describe('getApprovalRequirements', () => {
    it('should return approval requirements for judicial credentials', () => {
      const requirements = service.getApprovalRequirements([
        'VerifiableCredential',
        'JudicialCredential',
      ]);

      expect(requirements.requiresApproval).toBe(true);
      expect(requirements.approvalRoles).toContain('chief-judge');
      expect(requirements.requiresMultiSignature).toBe(true);
      expect(requirements.minSignatures).toBe(2);
    });

    it('should return no approval for identity credentials', () => {
      const requirements = service.getApprovalRequirements([
        'VerifiableCredential',
        'IdentityCredential',
      ]);

      expect(requirements.requiresApproval).toBe(false);
    });
  });

  describe('validateApproval', () => {
    it('should validate approval request', async () => {
      const approvalRequest = {
        credentialId: 'cred-1',
        requestedBy: 'user-1',
        requestedAt: new Date(),
        credentialType: ['VerifiableCredential', 'JudicialCredential'],
        subjectDid: 'did:web:example.com:subject-1',
        status: 'pending' as const,
        approvals: [],
        rejections: [],
      };

      const approver: AuthUser = {
        id: 'approver-1',
        email: 'approver@example.com',
        roles: ['chief-judge'],
        did: 'did:web:example.com:approver-1',
      };

      const result = await service.validateApproval(approvalRequest, approver);

      expect(result.valid).toBe(true);
    });

    it('should reject approval from unauthorized role', async () => {
      const approvalRequest = {
        credentialId: 'cred-1',
        requestedBy: 'user-1',
        requestedAt: new Date(),
        credentialType: ['VerifiableCredential', 'JudicialCredential'],
        subjectDid: 'did:web:example.com:subject-1',
        status: 'pending' as const,
        approvals: [],
        rejections: [],
      };

      const approver: AuthUser = {
        id: 'approver-1',
        email: 'approver@example.com',
        roles: ['user'],
        did: 'did:web:example.com:approver-1',
      };

      const result = await service.validateApproval(approvalRequest, approver);

      expect(result.valid).toBe(false);
      expect(result.reason).toContain('required role');
    });
  });

  describe('hasSufficientApprovals', () => {
    it('should return true for sufficient multi-signature approvals', () => {
      const approvalRequest = {
        credentialId: 'cred-1',
        requestedBy: 'user-1',
        requestedAt: new Date(),
        credentialType: ['VerifiableCredential', 'JudicialCredential'],
        subjectDid: 'did:web:example.com:subject-1',
        status: 'pending' as const,
        approvals: [
          {
            approverId: 'approver-1',
            approverRole: 'chief-judge',
            approvedAt: new Date(),
          },
          {
            approverId: 'approver-2',
            approverRole: 'judicial-council',
            approvedAt: new Date(),
          },
        ],
        rejections: [],
      };

      const result = service.hasSufficientApprovals(approvalRequest);

      expect(result).toBe(true);
    });

    it('should return false for insufficient approvals', () => {
      const approvalRequest = {
        credentialId: 'cred-1',
        requestedBy: 'user-1',
        requestedAt: new Date(),
        credentialType: ['VerifiableCredential', 'JudicialCredential'],
        subjectDid: 'did:web:example.com:subject-1',
        status: 'pending' as const,
        approvals: [
          {
            approverId: 'approver-1',
            approverRole: 'chief-judge',
            approvedAt: new Date(),
          },
        ],
        rejections: [],
      };

      const result = service.hasSufficientApprovals(approvalRequest);

      expect(result).toBe(false);
    });
  });

  describe('getAuthorizationService', () => {
    it('should return singleton instance', () => {
      const service1 = getAuthorizationService();
      const service2 = getAuthorizationService();

      expect(service1).toBe(service2);
    });
  });
});

