/**
 * @fileoverview Tests for QualityMetricsMapper
 */

import { describe, it, expect } from 'vitest';
import { QualityMetricsMapper } from '../quality-metrics.mapper.js';
import {
  RepositoryMetric,
  MetricShortcode,
  MetricDirection,
  MetricThresholdStatus,
} from '../../../models/metrics.js';
import { QualityMetrics } from '../../../domain/aggregates/quality-metrics/quality-metrics.aggregate.js';

describe('QualityMetricsMapper', () => {
  const mockApiMetric: RepositoryMetric = {
    name: 'Line Coverage',
    shortcode: MetricShortcode.LCV,
    description: 'Percentage of lines covered by tests',
    positiveDirection: MetricDirection.UPWARD,
    unit: '%',
    minValueAllowed: 0,
    maxValueAllowed: 100,
    isReported: true,
    isThresholdEnforced: true,
    items: [
      {
        id: 'item-1',
        key: 'AGGREGATE',
        threshold: 80,
        latestValue: 85.5,
        latestValueDisplay: '85.5%',
        thresholdStatus: MetricThresholdStatus.PASSING,
      },
      {
        id: 'item-2',
        key: 'PYTHON',
        threshold: 75,
        latestValue: 70,
        latestValueDisplay: '70%',
        thresholdStatus: MetricThresholdStatus.FAILING,
      },
    ],
  };

  describe('toDomainList', () => {
    it('should map API metric with multiple items to multiple domain aggregates', () => {
      const projectKey = 'test-project';
      const repositoryId = 'repo-123';

      const metrics = QualityMetricsMapper.toDomainList(mockApiMetric, projectKey, repositoryId);

      expect(metrics).toHaveLength(2);
      expect(metrics[0]).toBeInstanceOf(QualityMetrics);
      expect(metrics[1]).toBeInstanceOf(QualityMetrics);
      expect(metrics[0].configuration.metricKey).toBe('AGGREGATE');
      expect(metrics[1].configuration.metricKey).toBe('PYTHON');
    });

    it('should handle empty items array', () => {
      const emptyMetric = { ...mockApiMetric, items: [] };
      const metrics = QualityMetricsMapper.toDomainList(emptyMetric, 'test-project', 'repo-123');

      expect(metrics).toEqual([]);
    });
  });

  describe('toDomain', () => {
    it('should map API metric item to domain aggregate', () => {
      const projectKey = 'test-project';
      const repositoryId = 'repo-123';
      const item = mockApiMetric.items[0];

      const metrics = QualityMetricsMapper.toDomain(mockApiMetric, item, projectKey, repositoryId);

      expect(metrics).toBeInstanceOf(QualityMetrics);
      expect(metrics.projectKey).toBe(projectKey);
      expect(metrics.repositoryId).toBe(repositoryId);
      expect(metrics.configuration.name).toBe('Line Coverage');
      expect(metrics.configuration.shortcode).toBe(MetricShortcode.LCV);
      expect(metrics.configuration.metricKey).toBe('AGGREGATE');
    });

    it('should map threshold correctly', () => {
      const item = mockApiMetric.items[0];
      const metrics = QualityMetricsMapper.toDomain(
        mockApiMetric,
        item,
        'test-project',
        'repo-123'
      );

      expect(metrics.configuration.threshold).not.toBeNull();
      expect(metrics.configuration.threshold?.value).toBe(80);
    });

    it('should handle null threshold', () => {
      const itemNoThreshold = { ...mockApiMetric.items[0], threshold: null };
      const metrics = QualityMetricsMapper.toDomain(
        mockApiMetric,
        itemNoThreshold,
        'test-project',
        'repo-123'
      );

      expect(metrics.configuration.threshold).toBeNull();
    });

    it('should map current value correctly', () => {
      const item = mockApiMetric.items[0];
      const metrics = QualityMetricsMapper.toDomain(
        mockApiMetric,
        item,
        'test-project',
        'repo-123'
      );

      expect(metrics.currentValue).not.toBeNull();
      expect(metrics.currentValue?.value).toBe(85.5);
      expect(metrics.currentValue?.unit).toBe('%');
    });

    it('should handle null latest value', () => {
      const itemNoValue = { ...mockApiMetric.items[0], latestValue: null };
      const metrics = QualityMetricsMapper.toDomain(
        mockApiMetric,
        itemNoValue,
        'test-project',
        'repo-123'
      );

      expect(metrics.currentValue).toBeNull();
    });

    it('should map configuration properties correctly', () => {
      const item = mockApiMetric.items[0];
      const metrics = QualityMetricsMapper.toDomain(
        mockApiMetric,
        item,
        'test-project',
        'repo-123'
      );

      expect(metrics.configuration.unit).toBe('%');
      expect(metrics.configuration.minAllowed).toBe(0);
      expect(metrics.configuration.maxAllowed).toBe(100);
      expect(metrics.configuration.positiveDirection).toBe('UPWARD');
      expect(metrics.configuration.isReported).toBe(true);
      expect(metrics.configuration.isThresholdEnforced).toBe(true);
    });
  });

  describe('mapThresholdStatus', () => {
    it('should map API status to domain status', () => {
      expect(QualityMetricsMapper.mapThresholdStatus('PASSING')).toBe('PASSING');
      expect(QualityMetricsMapper.mapThresholdStatus('FAILING')).toBe('FAILING');
      expect(QualityMetricsMapper.mapThresholdStatus('UNKNOWN')).toBe('UNKNOWN');
    });

    it('should default to UNKNOWN for unmapped status', () => {
      expect(QualityMetricsMapper.mapThresholdStatus('INVALID')).toBe('UNKNOWN');
    });
  });

  describe('toPersistence', () => {
    it('should map domain aggregate to persistence format', () => {
      const item = mockApiMetric.items[0];
      const metrics = QualityMetricsMapper.toDomain(
        mockApiMetric,
        item,
        'test-project',
        'repo-123'
      );

      const persistence = QualityMetricsMapper.toPersistence(metrics);

      expect(persistence.id).toBe(metrics.id);
      expect(persistence.projectKey).toBe('test-project');
      expect(persistence.repositoryId).toBe('repo-123');
      expect(persistence.configuration).toEqual(metrics.configuration);
      expect(persistence.currentValue).toEqual(metrics.currentValue);
    });
  });

  describe('createHistoryEntry', () => {
    it('should create a history entry with threshold', () => {
      const entry = QualityMetricsMapper.createHistoryEntry(
        85.5,
        80,
        'commit-123',
        new Date('2024-01-15T10:00:00Z'),
        '%'
      );

      expect(entry.value.value).toBe(85.5);
      expect(entry.value.unit).toBe('%');
      expect(entry.threshold?.value).toBe(80);
      expect(entry.thresholdStatus).toBe('PASSING');
      expect(entry.commitOid).toBe('commit-123');
      expect(entry.recordedAt).toEqual(new Date('2024-01-15T10:00:00Z'));
    });

    it('should create a history entry without threshold', () => {
      const entry = QualityMetricsMapper.createHistoryEntry(
        85.5,
        null,
        'commit-123',
        new Date('2024-01-15T10:00:00Z'),
        '%'
      );

      expect(entry.threshold).toBeNull();
      expect(entry.thresholdStatus).toBe('UNKNOWN');
    });

    it('should determine failing status when value is below threshold', () => {
      const entry = QualityMetricsMapper.createHistoryEntry(
        75,
        80,
        'commit-123',
        new Date('2024-01-15T10:00:00Z'),
        '%'
      );

      expect(entry.thresholdStatus).toBe('FAILING');
    });
  });

  describe('toDomainFromList', () => {
    it('should map multiple API metrics to domain aggregates', () => {
      const apiMetrics = [
        mockApiMetric,
        {
          ...mockApiMetric,
          shortcode: MetricShortcode.BCV,
          name: 'Branch Coverage',
          items: [
            {
              id: 'item-3',
              key: 'AGGREGATE',
              threshold: 70,
              latestValue: 75,
              latestValueDisplay: '75%',
              thresholdStatus: MetricThresholdStatus.PASSING,
            },
          ],
        },
      ];

      const metrics = QualityMetricsMapper.toDomainFromList(apiMetrics, 'test-project', 'repo-123');

      expect(metrics).toHaveLength(3); // 2 from first metric, 1 from second
      expect(metrics[0].configuration.shortcode).toBe(MetricShortcode.LCV);
      expect(metrics[2].configuration.shortcode).toBe(MetricShortcode.BCV);
    });

    it('should handle empty metrics list', () => {
      const metrics = QualityMetricsMapper.toDomainFromList([], 'test-project', 'repo-123');
      expect(metrics).toEqual([]);
    });
  });
});
