/**
 * System Config Service Tests
 *
 * Unit tests for SystemConfigService
 */

import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import { PrismaClient } from '@prisma/client';
import { SystemConfigService } from '../src/modules/system-config/system-config.service';

const prisma = new PrismaClient();
const service = new SystemConfigService(prisma);

describe('SystemConfigService', () => {
  beforeAll(async () => {
    // Clean up test configs
    await prisma.systemConfig.deleteMany({
      where: { key: { startsWith: 'test.' } },
    });
  });

  afterAll(async () => {
    // Clean up after tests
    await prisma.systemConfig.deleteMany({
      where: { key: { startsWith: 'test.' } },
    });
    await prisma.$disconnect();
  });

  describe('upsert', () => {
    it('should create a new config', async () => {
      await service.upsert({
        key: 'test.create',
        value: 'test-value',
        category: 'platform',
        dataType: 'string',
      });

      const value = await service.get('test.create');
      expect(value).toBe('test-value');
    });

    it('should update existing config', async () => {
      await service.upsert({
        key: 'test.update',
        value: 'original',
        category: 'platform',
      });

      await service.upsert({
        key: 'test.update',
        value: 'updated',
        category: 'platform',
      });

      const value = await service.get('test.update');
      expect(value).toBe('updated');
    });

    it('should track change history', async () => {
      await service.upsert({
        key: 'test.history',
        value: 'v1',
        category: 'platform',
      }, 'user-1');

      await service.upsert({
        key: 'test.history',
        value: 'v2',
        category: 'platform',
      }, 'user-2');

      const history = await service.getChangeHistory('test.history');
      expect(history).toHaveLength(1);
      expect(history[0].value).toBe('v1');
      expect(history[0].user).toBe('user-1');
    });
  });

  describe('getParsed', () => {
    it('should parse boolean values', async () => {
      await service.upsert({
        key: 'test.bool.true',
        value: 'true',
        category: 'platform',
        dataType: 'boolean',
      });

      await service.upsert({
        key: 'test.bool.false',
        value: 'false',
        category: 'platform',
        dataType: 'boolean',
      });

      expect(await service.getParsed('test.bool.true')).toBe(true);
      expect(await service.getParsed('test.bool.false')).toBe(false);
    });

    it('should parse number values', async () => {
      await service.upsert({
        key: 'test.number',
        value: '42.5',
        category: 'platform',
        dataType: 'number',
      });

      const value = await service.getParsed('test.number');
      expect(value).toBe(42.5);
    });

    it('should parse json values', async () => {
      await service.upsert({
        key: 'test.json',
        value: '{"key": "value", "num": 123}',
        category: 'platform',
        dataType: 'json',
      });

      const value = await service.getParsed('test.json');
      expect(value).toEqual({ key: 'value', num: 123 });
    });
  });

  describe('getByCategory', () => {
    it('should filter by category', async () => {
      await service.upsert({
        key: 'test.cat.platform',
        value: '1',
        category: 'platform',
        isPublic: true,
      });

      await service.upsert({
        key: 'test.cat.ai',
        value: '2',
        category: 'ai',
        isPublic: true,
      });

      const platformConfigs = await service.getByCategory('platform');
      expect(platformConfigs.some(c => c.key === 'test.cat.platform')).toBe(true);
      expect(platformConfigs.some(c => c.key === 'test.cat.ai')).toBe(false);
    });

    it('should filter public/private configs', async () => {
      await service.upsert({
        key: 'test.public',
        value: 'public',
        category: 'platform',
        isPublic: true,
      });

      await service.upsert({
        key: 'test.private',
        value: 'private',
        category: 'platform',
        isPublic: false,
      });

      const publicConfigs = await service.getByCategory('platform');
      expect(publicConfigs.some(c => c.key === 'test.public')).toBe(true);
      expect(publicConfigs.some(c => c.key === 'test.private')).toBe(false);

      const allConfigs = await service.getByCategory('platform', true);
      expect(allConfigs.some(c => c.key === 'test.private')).toBe(true);
    });
  });

  describe('getPublicConfigs', () => {
    it('should return only public configs', async () => {
      await service.upsert({
        key: 'test.public.all',
        value: 'visible',
        category: 'platform',
        isPublic: true,
      });

      await service.upsert({
        key: 'test.private.all',
        value: 'hidden',
        category: 'platform',
        isPublic: false,
      });

      const configs = await service.getPublicConfigs();
      expect(configs.some(c => c.key === 'test.public.all')).toBe(true);
      expect(configs.some(c => c.key === 'test.private.all')).toBe(false);
    });
  });

  describe('updateValue', () => {
    it('should update only the value', async () => {
      await service.upsert({
        key: 'test.update.value',
        value: 'original',
        description: 'Original description',
        category: 'platform',
      });

      await service.updateValue('test.update.value', 'new-value', 'admin-1');

      const config = await prisma.systemConfig.findUnique({
        where: { key: 'test.update.value' },
      });

      expect(config?.value).toBe('new-value');
      expect(config?.description).toBe('Original description');
      expect(config?.category).toBe('platform');
    });

    it('should throw error for non-existent key', async () => {
      await expect(
        service.updateValue('test.nonexistent', 'value')
      ).rejects.toThrow("Config with key 'test.nonexistent' not found");
    });
  });

  describe('delete', () => {
    it('should delete config', async () => {
      await service.upsert({
        key: 'test.delete',
        value: 'to-delete',
        category: 'platform',
      });

      await service.delete('test.delete');

      const value = await service.get('test.delete');
      expect(value).toBeNull();
    });
  });

  describe('parseValue', () => {
    it('should parse different data types correctly', () => {
      expect(service.parseValue('123', 'number')).toBe(123);
      expect(service.parseValue('45.67', 'number')).toBe(45.67);
      expect(service.parseValue('true', 'boolean')).toBe(true);
      expect(service.parseValue('1', 'boolean')).toBe(true);
      expect(service.parseValue('false', 'boolean')).toBe(false);
      expect(service.parseValue('{"a":1}', 'json')).toEqual({ a: 1 });
      expect(service.parseValue('2024-01-01', 'date')).toEqual(new Date('2024-01-01'));
      expect(service.parseValue('plain string', 'string')).toBe('plain string');
    });

    it('should handle invalid json gracefully', () => {
      expect(service.parseValue('invalid json', 'json')).toBe('invalid json');
    });
  });
});
