/**
 * @vitest-environment node
 */

import {
  MetricShortcode,
  MetricKey,
  MetricThresholdStatus,
  MetricDirection,
  MetricSetting,
  UpdateMetricThresholdParams,
  UpdateMetricSettingParams,
  MetricThresholdUpdateResponse,
  MetricSettingUpdateResponse,
  RepositoryMetricItem,
  RepositoryMetric,
  MetricHistoryValue,
  MetricHistoryParams,
  MetricHistoryResponse,
} from '../../models/metrics';

describe('Metrics Models', () => {
  describe('Enum Types', () => {
    describe('MetricShortcode', () => {
      it('should define the correct metric shortcodes', () => {
        expect(MetricShortcode.LCV).toBe('LCV'); // Line Coverage
        expect(MetricShortcode.BCV).toBe('BCV'); // Branch Coverage
        expect(MetricShortcode.DCV).toBe('DCV'); // Documentation Coverage
        expect(MetricShortcode.DDP).toBe('DDP'); // Duplicate Code Percentage
        expect(MetricShortcode.SCV).toBe('SCV'); // Statement Coverage
        expect(MetricShortcode.TCV).toBe('TCV'); // Type Coverage
        expect(MetricShortcode.CMP).toBe('CMP'); // Complexity
      });

      it('should have the correct number of values', () => {
        expect(Object.keys(MetricShortcode).length).toBe(7);
      });
    });

    describe('MetricKey', () => {
      it('should define the correct metric keys', () => {
        expect(MetricKey.AGGREGATE).toBe('AGGREGATE');
        expect(MetricKey.PYTHON).toBe('PYTHON');
        expect(MetricKey.JAVASCRIPT).toBe('JAVASCRIPT');
        expect(MetricKey.TYPESCRIPT).toBe('TYPESCRIPT');
        expect(MetricKey.GO).toBe('GO');
        expect(MetricKey.JAVA).toBe('JAVA');
        expect(MetricKey.RUBY).toBe('RUBY');
        expect(MetricKey.RUST).toBe('RUST');
      });

      it('should have the correct number of values', () => {
        expect(Object.keys(MetricKey).length).toBe(8);
      });
    });

    describe('MetricThresholdStatus', () => {
      it('should define the correct threshold statuses', () => {
        expect(MetricThresholdStatus.PASSING).toBe('PASSING');
        expect(MetricThresholdStatus.FAILING).toBe('FAILING');
        expect(MetricThresholdStatus.UNKNOWN).toBe('UNKNOWN');
      });

      it('should have the correct number of values', () => {
        expect(Object.keys(MetricThresholdStatus).length).toBe(3);
      });
    });

    describe('MetricDirection', () => {
      it('should define the correct metric directions', () => {
        expect(MetricDirection.UPWARD).toBe('UPWARD');
        expect(MetricDirection.DOWNWARD).toBe('DOWNWARD');
      });

      it('should have the correct number of values', () => {
        expect(Object.keys(MetricDirection).length).toBe(2);
      });
    });
  });

  describe('Interface Type Checking', () => {
    describe('MetricSetting', () => {
      it('should validate a correct metric setting object', () => {
        const metricSetting: MetricSetting = {
          isReported: true,
          isThresholdEnforced: false,
        };

        // TypeScript type checking will ensure this compiles correctly
        expect(metricSetting.isReported).toBe(true);
        expect(metricSetting.isThresholdEnforced).toBe(false);
      });
    });

    describe('UpdateMetricThresholdParams', () => {
      it('should validate a correct update threshold params object', () => {
        const updateParams: UpdateMetricThresholdParams = {
          repositoryId: 'repo_123',
          metricShortcode: MetricShortcode.LCV,
          metricKey: MetricKey.PYTHON,
          thresholdValue: 85,
        };

        expect(updateParams.repositoryId).toBe('repo_123');
        expect(updateParams.metricShortcode).toBe(MetricShortcode.LCV);
        expect(updateParams.metricKey).toBe(MetricKey.PYTHON);
        expect(updateParams.thresholdValue).toBe(85);
      });

      it('should allow thresholdValue to be null', () => {
        const updateParams: UpdateMetricThresholdParams = {
          repositoryId: 'repo_123',
          metricShortcode: MetricShortcode.LCV,
          metricKey: MetricKey.PYTHON,
          thresholdValue: null,
        };

        expect(updateParams.thresholdValue).toBeNull();
      });
    });

    describe('UpdateMetricSettingParams', () => {
      it('should validate a correct update setting params object', () => {
        const updateParams: UpdateMetricSettingParams = {
          repositoryId: 'repo_123',
          metricShortcode: MetricShortcode.BCV,
          isReported: true,
          isThresholdEnforced: false,
        };

        expect(updateParams.repositoryId).toBe('repo_123');
        expect(updateParams.metricShortcode).toBe(MetricShortcode.BCV);
        expect(updateParams.isReported).toBe(true);
        expect(updateParams.isThresholdEnforced).toBe(false);
      });
    });

    describe('Response Interfaces', () => {
      it('should validate MetricThresholdUpdateResponse', () => {
        const response: MetricThresholdUpdateResponse = {
          ok: true,
        };

        expect(response.ok).toBe(true);
      });

      it('should validate MetricSettingUpdateResponse', () => {
        const response: MetricSettingUpdateResponse = {
          ok: false,
        };

        expect(response.ok).toBe(false);
      });
    });

    describe('RepositoryMetricItem', () => {
      it('should validate a valid metric item with threshold', () => {
        const metricItem: RepositoryMetricItem = {
          id: 'metric_123',
          key: MetricKey.TYPESCRIPT,
          threshold: 80,
          latestValue: 75.5,
          latestValueDisplay: '75.5%',
          thresholdStatus: MetricThresholdStatus.FAILING,
        };

        expect(metricItem.id).toBe('metric_123');
        expect(metricItem.key).toBe(MetricKey.TYPESCRIPT);
        expect(metricItem.threshold).toBe(80);
        expect(metricItem.latestValue).toBe(75.5);
        expect(metricItem.latestValueDisplay).toBe('75.5%');
        expect(metricItem.thresholdStatus).toBe(MetricThresholdStatus.FAILING);
      });

      it('should validate a valid metric item without threshold', () => {
        const metricItem: RepositoryMetricItem = {
          id: 'metric_123',
          key: MetricKey.TYPESCRIPT,
          threshold: null,
          latestValue: 75.5,
          latestValueDisplay: '75.5%',
          thresholdStatus: MetricThresholdStatus.UNKNOWN,
        };

        expect(metricItem.threshold).toBeNull();
      });

      it('should validate a valid metric item without latest value', () => {
        const metricItem: RepositoryMetricItem = {
          id: 'metric_123',
          key: MetricKey.TYPESCRIPT,
          threshold: 80,
          latestValue: null,
          latestValueDisplay: 'N/A',
          thresholdStatus: MetricThresholdStatus.UNKNOWN,
        };

        expect(metricItem.latestValue).toBeNull();
        expect(metricItem.latestValueDisplay).toBe('N/A');
      });
    });

    describe('RepositoryMetric', () => {
      it('should validate a complete repository metric object', () => {
        const metric: 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: 'metric_agg_123',
              key: MetricKey.AGGREGATE,
              threshold: 80,
              latestValue: 78.5,
              latestValueDisplay: '78.5%',
              thresholdStatus: MetricThresholdStatus.FAILING,
            },
            {
              id: 'metric_py_123',
              key: MetricKey.PYTHON,
              threshold: 75,
              latestValue: 82.3,
              latestValueDisplay: '82.3%',
              thresholdStatus: MetricThresholdStatus.PASSING,
            },
          ],
        };

        expect(metric.name).toBe('Line Coverage');
        expect(metric.shortcode).toBe(MetricShortcode.LCV);
        expect(metric.positiveDirection).toBe(MetricDirection.UPWARD);
        expect(metric.items.length).toBe(2);
        expect(metric.items[0].key).toBe(MetricKey.AGGREGATE);
        expect(metric.items[1].key).toBe(MetricKey.PYTHON);
      });
    });

    describe('MetricHistoryValue', () => {
      it('should validate a valid metric history value', () => {
        const historyValue: MetricHistoryValue = {
          value: 85.5,
          valueDisplay: '85.5%',
          threshold: 80,
          thresholdStatus: MetricThresholdStatus.PASSING,
          commitOid: 'abc123def456',
          createdAt: '2023-01-15T12:30:45Z',
        };

        expect(historyValue.value).toBe(85.5);
        expect(historyValue.valueDisplay).toBe('85.5%');
        expect(historyValue.threshold).toBe(80);
        expect(historyValue.thresholdStatus).toBe(MetricThresholdStatus.PASSING);
        expect(historyValue.commitOid).toBe('abc123def456');
        expect(historyValue.createdAt).toBe('2023-01-15T12:30:45Z');
      });

      it('should allow optional fields to be undefined', () => {
        const historyValue: MetricHistoryValue = {
          value: 85.5,
          valueDisplay: '85.5%',
          commitOid: 'abc123def456',
          createdAt: '2023-01-15T12:30:45Z',
        };

        expect(historyValue.threshold).toBeUndefined();
        expect(historyValue.thresholdStatus).toBeUndefined();
      });

      it('should allow threshold to be null', () => {
        const historyValue: MetricHistoryValue = {
          value: 85.5,
          valueDisplay: '85.5%',
          threshold: null,
          thresholdStatus: MetricThresholdStatus.UNKNOWN,
          commitOid: 'abc123def456',
          createdAt: '2023-01-15T12:30:45Z',
        };

        expect(historyValue.threshold).toBeNull();
      });
    });

    describe('MetricHistoryParams', () => {
      it('should validate a minimal parameter set', () => {
        const params: MetricHistoryParams = {
          projectKey: 'project_123',
          metricShortcode: MetricShortcode.LCV,
          metricKey: MetricKey.AGGREGATE,
        };

        expect(params.projectKey).toBe('project_123');
        expect(params.metricShortcode).toBe(MetricShortcode.LCV);
        expect(params.metricKey).toBe(MetricKey.AGGREGATE);
      });

      it('should validate a complete parameter set', () => {
        const params: MetricHistoryParams = {
          projectKey: 'project_123',
          metricShortcode: MetricShortcode.LCV,
          metricKey: MetricKey.AGGREGATE,
          startDate: '2023-01-01T00:00:00Z',
          endDate: '2023-02-01T00:00:00Z',
          limit: 30,
        };

        expect(params.startDate).toBe('2023-01-01T00:00:00Z');
        expect(params.endDate).toBe('2023-02-01T00:00:00Z');
        expect(params.limit).toBe(30);
      });
    });

    describe('MetricHistoryResponse', () => {
      it('should validate a valid history response', () => {
        const response: MetricHistoryResponse = {
          shortcode: MetricShortcode.LCV,
          metricKey: MetricKey.PYTHON,
          name: 'Line Coverage',
          unit: '%',
          positiveDirection: MetricDirection.UPWARD,
          threshold: 80,
          isTrendingPositive: true,
          values: [
            {
              value: 78.5,
              valueDisplay: '78.5%',
              threshold: 80,
              thresholdStatus: MetricThresholdStatus.FAILING,
              commitOid: 'abc123',
              createdAt: '2023-01-01T00:00:00Z',
            },
            {
              value: 85.2,
              valueDisplay: '85.2%',
              threshold: 80,
              thresholdStatus: MetricThresholdStatus.PASSING,
              commitOid: 'def456',
              createdAt: '2023-01-15T00:00:00Z',
            },
          ],
        };

        expect(response.shortcode).toBe(MetricShortcode.LCV);
        expect(response.metricKey).toBe(MetricKey.PYTHON);
        expect(response.name).toBe('Line Coverage');
        expect(response.threshold).toBe(80);
        expect(response.isTrendingPositive).toBe(true);
        expect(response.values.length).toBe(2);
        expect(response.values[0].value).toBe(78.5);
        expect(response.values[1].thresholdStatus).toBe(MetricThresholdStatus.PASSING);
      });

      it('should allow threshold to be null', () => {
        const response: MetricHistoryResponse = {
          shortcode: MetricShortcode.LCV,
          metricKey: MetricKey.PYTHON,
          name: 'Line Coverage',
          unit: '%',
          positiveDirection: MetricDirection.UPWARD,
          threshold: null,
          isTrendingPositive: true,
          values: [],
        };

        expect(response.threshold).toBeNull();
      });
    });
  });

  // Additional test for serialization and deserialization
  describe('JSON Serialization & Deserialization', () => {
    it('should correctly serialize and deserialize metric objects', () => {
      const original: 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: 'metric_agg_123',
            key: MetricKey.AGGREGATE,
            threshold: 80,
            latestValue: 78.5,
            latestValueDisplay: '78.5%',
            thresholdStatus: MetricThresholdStatus.FAILING,
          },
        ],
      };

      const json = JSON.stringify(original);
      const deserialized = JSON.parse(json) as RepositoryMetric;

      expect(deserialized).toEqual(original);
      expect(deserialized.shortcode).toBe(MetricShortcode.LCV);
      expect(deserialized.positiveDirection).toBe(MetricDirection.UPWARD);
      expect(deserialized.items[0].thresholdStatus).toBe(MetricThresholdStatus.FAILING);
    });

    it('should handle nullable fields during serialization', () => {
      const original: RepositoryMetricItem = {
        id: 'metric_123',
        key: MetricKey.TYPESCRIPT,
        threshold: null,
        latestValue: null,
        latestValueDisplay: 'N/A',
        thresholdStatus: MetricThresholdStatus.UNKNOWN,
      };

      const json = JSON.stringify(original);
      const deserialized = JSON.parse(json) as RepositoryMetricItem;

      expect(deserialized.threshold).toBeNull();
      expect(deserialized.latestValue).toBeNull();
    });
  });
});
