/**
 * Credential Templates Tests
 */

import { describe, it, expect, vi, beforeEach } from 'vitest';
import {
  createCredentialTemplate,
  getCredentialTemplate,
  getCredentialTemplateByName,
  listCredentialTemplates,
  updateCredentialTemplate,
  createTemplateVersion,
  renderCredentialFromTemplate,
  CredentialTemplate,
} from './credential-templates';
import { query } from './client';

vi.mock('./client');

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

  describe('createCredentialTemplate', () => {
    it('should create credential template', async () => {
      const mockTemplate = {
        name: 'test-template',
        description: 'Test template',
        credential_type: ['VerifiableCredential', 'IdentityCredential'],
        template_data: { field: 'value' },
        version: 1,
        is_active: true,
        created_by: 'admin-user-id',
      };

      const mockResult = {
        rows: [
          {
            id: 'template-id',
            ...mockTemplate,
            created_at: new Date(),
            updated_at: new Date(),
          },
        ],
      };

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

      const result = await createCredentialTemplate(mockTemplate);

      expect(result).toEqual(mockResult.rows[0]);
      expect(query).toHaveBeenCalledWith(
        expect.stringContaining('INSERT INTO credential_templates'),
        expect.arrayContaining([
          mockTemplate.name,
          mockTemplate.description,
          mockTemplate.credential_type,
          JSON.stringify(mockTemplate.template_data),
          mockTemplate.version,
          mockTemplate.is_active,
          mockTemplate.created_by,
        ])
      );
    });
  });

  describe('getCredentialTemplate', () => {
    it('should get credential template by ID', async () => {
      const mockTemplate = {
        id: 'template-id',
        name: 'test-template',
        credential_type: ['VerifiableCredential'],
        template_data: { field: 'value' },
        version: 1,
        is_active: true,
        created_at: new Date(),
        updated_at: new Date(),
      };

      const mockResult = {
        rows: [mockTemplate],
      };

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

      const result = await getCredentialTemplate('template-id');

      expect(result).toEqual(mockTemplate);
      expect(query).toHaveBeenCalledWith(
        expect.stringContaining('SELECT * FROM credential_templates'),
        ['template-id']
      );
    });

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

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

      const result = await getCredentialTemplate('non-existent-id');

      expect(result).toBeNull();
    });
  });

  describe('getCredentialTemplateByName', () => {
    it('should get credential template by name with version', async () => {
      const mockTemplate = {
        id: 'template-id',
        name: 'test-template',
        version: 2,
        credential_type: ['VerifiableCredential'],
        template_data: { field: 'value' },
        is_active: true,
        created_at: new Date(),
        updated_at: new Date(),
      };

      const mockResult = {
        rows: [mockTemplate],
      };

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

      const result = await getCredentialTemplateByName('test-template', 2);

      expect(result).toEqual(mockTemplate);
      expect(query).toHaveBeenCalledWith(
        expect.stringContaining('SELECT * FROM credential_templates WHERE name = $1 AND version = $2'),
        ['test-template', 2]
      );
    });

    it('should get latest active version if version not specified', async () => {
      const mockTemplate = {
        id: 'template-id',
        name: 'test-template',
        version: 2,
        credential_type: ['VerifiableCredential'],
        template_data: { field: 'value' },
        is_active: true,
        created_at: new Date(),
        updated_at: new Date(),
      };

      const mockResult = {
        rows: [mockTemplate],
      };

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

      const result = await getCredentialTemplateByName('test-template');

      expect(result).toEqual(mockTemplate);
      expect(query).toHaveBeenCalledWith(
        expect.stringContaining('SELECT * FROM credential_templates WHERE name = $1 AND is_active = TRUE'),
        ['test-template']
      );
    });
  });

  describe('listCredentialTemplates', () => {
    it('should list active credential templates', async () => {
      const mockTemplates = [
        {
          id: 'template-id',
          name: 'test-template',
          credential_type: ['VerifiableCredential'],
          template_data: { field: 'value' },
          version: 1,
          is_active: true,
          created_at: new Date(),
          updated_at: new Date(),
        },
      ];

      const mockResult = {
        rows: mockTemplates,
      };

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

      const result = await listCredentialTemplates(true, 100, 0);

      expect(result).toEqual(mockTemplates);
      expect(query).toHaveBeenCalledWith(
        expect.stringContaining('SELECT * FROM credential_templates'),
        [100, 0]
      );
    });

    it('should list all credential templates if activeOnly is false', async () => {
      const mockTemplates = [
        {
          id: 'template-id',
          name: 'test-template',
          credential_type: ['VerifiableCredential'],
          template_data: { field: 'value' },
          version: 1,
          is_active: false,
          created_at: new Date(),
          updated_at: new Date(),
        },
      ];

      const mockResult = {
        rows: mockTemplates,
      };

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

      const result = await listCredentialTemplates(false, 100, 0);

      expect(result).toEqual(mockTemplates);
      expect(query).toHaveBeenCalledWith(
        expect.stringContaining('SELECT * FROM credential_templates'),
        [100, 0]
      );
    });
  });

  describe('updateCredentialTemplate', () => {
    it('should update credential template', async () => {
      const mockUpdates = {
        description: 'Updated description',
        template_data: { field: 'updated-value' },
        is_active: false,
      };

      const mockTemplate = {
        id: 'template-id',
        name: 'test-template',
        ...mockUpdates,
        credential_type: ['VerifiableCredential'],
        version: 1,
        created_at: new Date(),
        updated_at: new Date(),
      };

      const mockResult = {
        rows: [mockTemplate],
      };

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

      const result = await updateCredentialTemplate('template-id', mockUpdates);

      expect(result).toEqual(mockTemplate);
      expect(query).toHaveBeenCalledWith(
        expect.stringContaining('UPDATE credential_templates'),
        expect.arrayContaining([
          mockUpdates.description,
          JSON.stringify(mockUpdates.template_data),
          mockUpdates.is_active,
          'template-id',
        ])
      );
    });
  });

  describe('createTemplateVersion', () => {
    it('should create new template version', async () => {
      const mockVersion = {
        template_data: { field: 'new-version-value' },
        description: 'New version description',
      };

      const mockTemplate = {
        id: 'template-id',
        name: 'test-template',
        credential_type: ['VerifiableCredential'],
        template_data: mockVersion.template_data,
        version: 2,
        is_active: true,
        created_at: new Date(),
        updated_at: new Date(),
      };

      // Mock queries: get current template, then create new version
      vi.mocked(query)
        .mockResolvedValueOnce({
          rows: [
            {
              id: 'template-id',
              name: 'test-template',
              credential_type: ['VerifiableCredential'],
              template_data: { field: 'old-value' },
              version: 1,
              is_active: true,
            },
          ],
        } as any)
        .mockResolvedValueOnce({
          rows: [mockTemplate],
        } as any);

      const result = await createTemplateVersion('template-id', mockVersion);

      expect(result).toEqual(mockTemplate);
      expect(query).toHaveBeenCalledTimes(2);
    });
  });

  describe('renderCredentialFromTemplate', () => {
    it('should render credential from template with variables', () => {
      const mockTemplate: CredentialTemplate = {
        id: 'template-id',
        name: 'test-template',
        credential_type: ['VerifiableCredential'],
        template_data: {
          name: '{{name}}',
          email: '{{email}}',
          role: '{{role}}',
        },
        version: 1,
        is_active: true,
        created_at: new Date(),
        updated_at: new Date(),
      };

      const variables = {
        name: 'John Doe',
        email: 'john@example.com',
        role: 'admin',
      };

      const result = renderCredentialFromTemplate(mockTemplate, variables);

      expect(result).toEqual({
        name: 'John Doe',
        email: 'john@example.com',
        role: 'admin',
      });
    });

    it('should handle missing variables', () => {
      const mockTemplate: CredentialTemplate = {
        id: 'template-id',
        name: 'test-template',
        credential_type: ['VerifiableCredential'],
        template_data: {
          name: '{{name}}',
          email: '{{email}}',
        },
        version: 1,
        is_active: true,
        created_at: new Date(),
        updated_at: new Date(),
      };

      const variables = {
        name: 'John Doe',
      };

      const result = renderCredentialFromTemplate(mockTemplate, variables);

      expect(result).toEqual({
        name: 'John Doe',
        email: '{{email}}', // Unresolved variable remains as-is
      });
    });
  });
});

