/**
 * @fileoverview Tests for AnalysisRunMapper
 */

import { describe, it, expect } from 'vitest';
import { AnalysisRunMapper } from '../analysis-run.mapper.js';
import { DeepSourceRun, AnalysisRunStatus } from '../../../models/runs.js';
import {
  asRunId,
  asCommitOid,
  asBranchName,
  asAnalyzerShortcode,
  asGraphQLNodeId,
} from '../../../types/branded.js';
import { AnalysisRun } from '../../../domain/aggregates/analysis-run/analysis-run.aggregate.js';

describe('AnalysisRunMapper', () => {
  const mockApiRun: DeepSourceRun = {
    id: asGraphQLNodeId('R_kgDOGHqVWg'),
    runUid: asRunId('run-123'),
    commitOid: asCommitOid('abc123def456'),
    branchName: asBranchName('main'),
    baseOid: asCommitOid('def456ghi789'),
    status: 'SUCCESS',
    createdAt: '2024-01-15T10:00:00Z',
    updatedAt: '2024-01-15T10:05:00Z',
    finishedAt: '2024-01-15T10:10:00Z',
    summary: {
      occurrencesIntroduced: 10,
      occurrencesResolved: 5,
      occurrencesSuppressed: 2,
      occurrenceDistributionByAnalyzer: [
        {
          analyzerShortcode: asAnalyzerShortcode('python'),
          introduced: 7,
        },
        {
          analyzerShortcode: asAnalyzerShortcode('javascript'),
          introduced: 3,
        },
      ],
      occurrenceDistributionByCategory: [
        {
          category: 'bug_risk',
          introduced: 4,
        },
        {
          category: 'SECURITY',
          introduced: 3,
        },
        {
          category: 'performance',
          introduced: 3,
        },
      ],
    },
    repository: {
      name: 'test-repo',
      id: asGraphQLNodeId('R_repo123'),
    },
  };

  describe('toDomain', () => {
    it('should map API run to domain aggregate', () => {
      const projectKey = 'test-project';
      const run = AnalysisRunMapper.toDomain(mockApiRun, projectKey);

      expect(run).toBeInstanceOf(AnalysisRun);
      expect(run.runId).toBe('run-123');
      expect(run.projectKey).toBe(projectKey);
      expect(run.repositoryId).toBe('R_repo123');
      expect(run.status).toBe('SUCCESS');
    });

    it('should map commit information correctly', () => {
      const projectKey = 'test-project';
      const run = AnalysisRunMapper.toDomain(mockApiRun, projectKey);

      expect(run.commitInfo.oid).toBe('abc123def456');
      expect(run.commitInfo.branch).toBe('main');
      expect(run.commitInfo.baseOid).toBe('def456ghi789');
    });

    it('should map timestamps correctly', () => {
      const projectKey = 'test-project';
      const run = AnalysisRunMapper.toDomain(mockApiRun, projectKey);

      expect(run.timestamps.createdAt).toEqual(new Date('2024-01-15T10:00:00Z'));
      expect(run.timestamps.startedAt).toEqual(new Date('2024-01-15T10:05:00Z'));
      expect(run.timestamps.finishedAt).toEqual(new Date('2024-01-15T10:10:00Z'));
    });

    it('should map issue summary correctly', () => {
      const projectKey = 'test-project';
      const run = AnalysisRunMapper.toDomain(mockApiRun, projectKey);

      expect(run.summary).toBeDefined();
      expect(run.summary.totalIntroduced.count).toBe(10);
      expect(run.summary.totalResolved.count).toBe(5);
      expect(run.summary.totalSuppressed.count).toBe(2);
    });

    it('should map analyzer distributions', () => {
      const projectKey = 'test-project';
      const run = AnalysisRunMapper.toDomain(mockApiRun, projectKey);

      expect(run.summary?.byAnalyzer).toHaveLength(2);
      expect(run.summary?.byAnalyzer[0].analyzerShortcode).toBe('python');
      expect(run.summary?.byAnalyzer[0].introduced.count).toBe(7);
      expect(run.summary?.byAnalyzer[1].analyzerShortcode).toBe('javascript');
      expect(run.summary?.byAnalyzer[1].introduced.count).toBe(3);
    });

    it('should map category distributions with case normalization', () => {
      const projectKey = 'test-project';
      const run = AnalysisRunMapper.toDomain(mockApiRun, projectKey);

      expect(run.summary?.byCategory).toHaveLength(3);
      expect(run.summary?.byCategory[0].category).toBe('BUG_RISK');
      expect(run.summary?.byCategory[0].introduced.count).toBe(4);
      expect(run.summary?.byCategory[1].category).toBe('SECURITY');
      expect(run.summary?.byCategory[1].introduced.count).toBe(3);
      expect(run.summary?.byCategory[2].category).toBe('PERFORMANCE');
      expect(run.summary?.byCategory[2].introduced.count).toBe(3);
    });

    it('should handle API status mapping', () => {
      const testCases = [
        { apiStatus: 'PENDING', expectedStatus: 'PENDING' },
        { apiStatus: 'READY', expectedStatus: 'RUNNING' },
        { apiStatus: 'SUCCESS', expectedStatus: 'SUCCESS' },
        { apiStatus: 'FAILURE', expectedStatus: 'FAILURE' },
        { apiStatus: 'TIMEOUT', expectedStatus: 'TIMEOUT' },
        { apiStatus: 'CANCEL', expectedStatus: 'CANCELLED' },
        { apiStatus: 'SKIPPED', expectedStatus: 'SKIPPED' },
      ];

      for (const { apiStatus, expectedStatus } of testCases) {
        const apiRun = { ...mockApiRun, status: apiStatus as AnalysisRunStatus };
        const run = AnalysisRunMapper.toDomain(apiRun, 'test-project');
        expect(run.status).toBe(expectedStatus);
      }
    });

    it('should handle unknown status by defaulting to FAILURE', () => {
      const apiRun = { ...mockApiRun, status: 'UNKNOWN_STATUS' as AnalysisRunStatus };
      const run = AnalysisRunMapper.toDomain(apiRun, 'test-project');
      expect(run.status).toBe('FAILURE');
    });

    it('should handle PENDING status timestamps', () => {
      const apiRun = { ...mockApiRun, status: 'PENDING', finishedAt: undefined };
      const run = AnalysisRunMapper.toDomain(apiRun, 'test-project');

      expect(run.timestamps.startedAt).toBeUndefined();
      expect(run.timestamps.finishedAt).toBeUndefined();
    });

    it('should handle missing distributions', () => {
      const apiRun = {
        ...mockApiRun,
        summary: {
          occurrencesIntroduced: 10,
          occurrencesResolved: 5,
          occurrencesSuppressed: 2,
        },
      };
      const run = AnalysisRunMapper.toDomain(apiRun, 'test-project');

      expect(run.summary?.byAnalyzer).toEqual([]);
      expect(run.summary?.byCategory).toEqual([]);
    });

    it('should initialize issues as empty array', () => {
      const run = AnalysisRunMapper.toDomain(mockApiRun, 'test-project');
      expect(run.issues).toEqual([]);
    });
  });

  describe('toPersistence', () => {
    it('should map domain aggregate to persistence format', () => {
      const run = AnalysisRun.create({
        runId: asRunId('run-123'),
        projectKey: 'test-project',
        repositoryId: asGraphQLNodeId('R_repo123'),
        commitInfo: {
          oid: asCommitOid('abc123'),
          branch: asBranchName('main'),
          baseOid: asCommitOid('def456'),
        },
      });

      const persistence = AnalysisRunMapper.toPersistence(run);

      expect(persistence.runId).toBe('run-123');
      expect(persistence.projectKey).toBe('test-project');
      expect(persistence.repositoryId).toBe('R_repo123');
      expect(persistence.status).toBe('PENDING');
      expect(persistence.commitInfo.oid).toBe('abc123');
    });
  });

  describe('toDomainList', () => {
    it('should map multiple API runs to domain aggregates', () => {
      const apiRuns = [
        mockApiRun,
        { ...mockApiRun, runUid: asRunId('run-456'), status: 'FAILURE' },
        { ...mockApiRun, runUid: asRunId('run-789'), status: 'READY' },
      ];

      const runs = AnalysisRunMapper.toDomainList(apiRuns, 'test-project');

      expect(runs).toHaveLength(3);
      expect(runs[0].runId).toBe('run-123');
      expect(runs[0].status).toBe('SUCCESS');
      expect(runs[1].runId).toBe('run-456');
      expect(runs[1].status).toBe('FAILURE');
      expect(runs[2].runId).toBe('run-789');
      expect(runs[2].status).toBe('RUNNING');
    });

    it('should handle empty list', () => {
      const runs = AnalysisRunMapper.toDomainList([], 'test-project');
      expect(runs).toEqual([]);
    });
  });
});
