/**
 * Credential Lifecycle Tests
 */

import { describe, it, expect, vi, beforeEach } from 'vitest';
import {
  addCredentialStatusHistory,
  getCredentialStatusHistory,
  revokeCredential,
  isCredentialRevoked,
  getRevocationRegistry,
  logCredentialAction,
  getCredentialAuditLog,
  getExpiringCredentials,
} from './credential-lifecycle';
import { query } from './client';

vi.mock('./client');

describe('Credential Lifecycle', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  describe('addCredentialStatusHistory', () => {
    it('should add credential status history', async () => {
      const mockHistory = {
        credential_id: 'test-credential-id',
        status: 'issued',
        reason: 'Initial issuance',
        changed_by: 'admin-user-id',
        metadata: { source: 'automated' },
      };

      const mockResult = {
        rows: [
          {
            id: 'history-id',
            ...mockHistory,
            changed_at: new Date(),
          },
        ],
      };

      vi.mocked(query).mockResolvedValueOnce(mockResult as any);

      const result = await addCredentialStatusHistory(mockHistory);

      expect(result).toEqual(mockResult.rows[0]);
      expect(query).toHaveBeenCalledWith(
        expect.stringContaining('INSERT INTO credential_status_history'),
        expect.arrayContaining([
          mockHistory.credential_id,
          mockHistory.status,
          mockHistory.reason,
          mockHistory.changed_by,
          JSON.stringify(mockHistory.metadata),
        ])
      );
    });
  });

  describe('getCredentialStatusHistory', () => {
    it('should get credential status history', async () => {
      const mockHistory = [
        {
          id: 'history-id',
          credential_id: 'test-credential-id',
          status: 'issued',
          changed_at: new Date(),
        },
      ];

      const mockResult = {
        rows: mockHistory,
      };

      vi.mocked(query).mockResolvedValueOnce(mockResult as any);

      const result = await getCredentialStatusHistory('test-credential-id');

      expect(result).toEqual(mockHistory);
      expect(query).toHaveBeenCalledWith(
        expect.stringContaining('SELECT * FROM credential_status_history'),
        ['test-credential-id']
      );
    });
  });

  describe('revokeCredential', () => {
    it('should revoke credential', async () => {
      const mockRevocation = {
        credential_id: 'test-credential-id',
        issuer_did: 'did:web:example.com',
        revocation_reason: 'Security incident',
        revoked_by: 'admin-user-id',
      };

      const mockIndexResult = {
        rows: [{ max_index: 5 }],
      };

      const mockRevocationResult = {
        rows: [
          {
            id: 'revocation-id',
            ...mockRevocation,
            revoked_at: new Date(),
            revocation_list_index: 6,
          },
        ],
      };

      vi.mocked(query)
        .mockResolvedValueOnce({ rows: [] } as any) // UPDATE query
        .mockResolvedValueOnce(mockIndexResult as any) // MAX query
        .mockResolvedValueOnce(mockRevocationResult as any); // INSERT query

      const result = await revokeCredential(mockRevocation);

      expect(result.revocation_list_index).toBe(6);
      expect(query).toHaveBeenCalledTimes(3);
    });
  });

  describe('isCredentialRevoked', () => {
    it('should return true if credential is revoked', async () => {
      const mockResult = {
        rows: [{ revoked: true }],
      };

      vi.mocked(query).mockResolvedValueOnce(mockResult as any);

      const result = await isCredentialRevoked('test-credential-id');

      expect(result).toBe(true);
      expect(query).toHaveBeenCalledWith(
        expect.stringContaining('SELECT revoked FROM verifiable_credentials'),
        ['test-credential-id']
      );
    });

    it('should return false if credential is not revoked', async () => {
      const mockResult = {
        rows: [{ revoked: false }],
      };

      vi.mocked(query).mockResolvedValueOnce(mockResult as any);

      const result = await isCredentialRevoked('test-credential-id');

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

    it('should return false if credential not found', async () => {
      const mockResult = {
        rows: [],
      };

      vi.mocked(query).mockResolvedValueOnce(mockResult as any);

      const result = await isCredentialRevoked('test-credential-id');

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

  describe('getRevocationRegistry', () => {
    it('should get revocation registry', async () => {
      const mockRevocations = [
        {
          id: 'revocation-id',
          credential_id: 'test-credential-id',
          issuer_did: 'did:web:example.com',
          revoked_at: new Date(),
          revocation_list_index: 1,
        },
      ];

      const mockResult = {
        rows: mockRevocations,
      };

      vi.mocked(query).mockResolvedValueOnce(mockResult as any);

      const result = await getRevocationRegistry('did:web:example.com', 100, 0);

      expect(result).toEqual(mockRevocations);
      expect(query).toHaveBeenCalledWith(
        expect.stringContaining('SELECT * FROM credential_revocation_registry'),
        ['did:web:example.com', 100, 0]
      );
    });
  });

  describe('logCredentialAction', () => {
    it('should log credential action', async () => {
      const mockAudit = {
        credential_id: 'test-credential-id',
        issuer_did: 'did:web:example.com',
        subject_did: 'did:web:subject.com',
        credential_type: ['VerifiableCredential', 'IdentityCredential'],
        action: 'issued' as const,
        performed_by: 'admin-user-id',
        metadata: { source: 'automated' },
        ip_address: '127.0.0.1',
        user_agent: 'test-agent',
      };

      const mockResult = {
        rows: [
          {
            id: 'audit-id',
            ...mockAudit,
            performed_at: new Date(),
          },
        ],
      };

      vi.mocked(query).mockResolvedValueOnce(mockResult as any);

      const result = await logCredentialAction(mockAudit);

      expect(result).toEqual(mockResult.rows[0]);
      expect(query).toHaveBeenCalledWith(
        expect.stringContaining('INSERT INTO credential_issuance_audit'),
        expect.arrayContaining([
          mockAudit.credential_id,
          mockAudit.issuer_did,
          mockAudit.subject_did,
          mockAudit.credential_type,
          mockAudit.action,
          mockAudit.performed_by,
          JSON.stringify(mockAudit.metadata),
          mockAudit.ip_address,
          mockAudit.user_agent,
        ])
      );
    });
  });

  describe('getCredentialAuditLog', () => {
    it('should get credential audit log', async () => {
      const mockLogs = [
        {
          id: 'audit-id',
          credential_id: 'test-credential-id',
          action: 'issued' as const,
          performed_at: new Date(),
        },
      ];

      const mockResult = {
        rows: mockLogs,
      };

      vi.mocked(query).mockResolvedValueOnce(mockResult as any);

      const result = await getCredentialAuditLog('test-credential-id', 100);

      expect(result).toEqual(mockLogs);
      expect(query).toHaveBeenCalledWith(
        expect.stringContaining('SELECT * FROM credential_issuance_audit'),
        ['test-credential-id', 100]
      );
    });
  });

  describe('getExpiringCredentials', () => {
    it('should get expiring credentials', async () => {
      const mockCredentials = [
        {
          credential_id: 'test-credential-id',
          expiration_date: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000), // 30 days from now
          subject_did: 'did:web:subject.com',
          issuer_did: 'did:web:example.com',
          credential_type: ['VerifiableCredential'],
          credential_subject: {},
        },
      ];

      const mockResult = {
        rows: mockCredentials,
      };

      vi.mocked(query).mockResolvedValueOnce(mockResult as any);

      const result = await getExpiringCredentials(90, 100);

      expect(result).toEqual(mockCredentials);
      expect(query).toHaveBeenCalledWith(
        expect.stringContaining('SELECT credential_id, expiration_date'),
        [100]
      );
    });
  });
});

