import { Test, TestingModule } from '@nestjs/testing';
import { ConfigService } from '@nestjs/config';
import { AtlasProxyService } from '../atlas-proxy/atlas-proxy.service';
import {
  ScheduledJobsRuntimeClient,
  RunBundle,
  CreateRunInput,
  ReportRunInput,
} from './scheduled-jobs-runtime.client';

const DEFAULT_BASE_PATH = '/api/atlas/internal/scheduled-jobs-runtime';

describe('ScheduledJobsRuntimeClient', () => {
  let client: ScheduledJobsRuntimeClient;
  let forwardRequest: jest.Mock;

  beforeEach(async () => {
    forwardRequest = jest.fn();

    const module: TestingModule = await Test.createTestingModule({
      providers: [
        ScheduledJobsRuntimeClient,
        {
          provide: AtlasProxyService,
          useValue: { forwardRequest },
        },
        {
          provide: ConfigService,
          useValue: {
            get: jest.fn().mockReturnValue(undefined),
          },
        },
      ],
    }).compile();

    client = module.get<ScheduledJobsRuntimeClient>(ScheduledJobsRuntimeClient);
  });

  describe('getBundle', () => {
    it('should call forwardRequest with GET and the correct bundle path', async () => {
      const mockBundle: RunBundle = {
        jobId: 'job-1',
        versionId: 'v1',
        code: 'console.log("hello")',
        env: { NODE_ENV: 'production' },
        fetchAllowlist: ['https://api.example.com'],
        credential: null,
        limits: { timeoutMs: 30000, memoryMb: 128 },
        overlapPolicy: 'SKIP',
        maxRetries: 3,
        input: { foo: 'bar' },
      };

      forwardRequest.mockResolvedValueOnce(mockBundle);

      const result = await client.getBundle('job-1');

      expect(forwardRequest).toHaveBeenCalledWith(
        'GET',
        `${DEFAULT_BASE_PATH}/bundle/job-1`,
      );
      expect(result).toEqual(mockBundle);
    });
  });

  describe('createRun', () => {
    it('should call forwardRequest with POST, wrapped create payload, and return response.run', async () => {
      const input: CreateRunInput = {
        jobId: 'job-1',
        trigger: 'SCHEDULE',
        scheduledFor: '2026-06-24T10:00:00Z',
        attempt: 1,
      };

      const mockRun = { runId: 'run-abc-123' };
      forwardRequest.mockResolvedValueOnce({ run: mockRun });

      const result = await client.createRun(input);

      expect(forwardRequest).toHaveBeenCalledWith(
        'POST',
        `${DEFAULT_BASE_PATH}/runs`,
        { create: input },
      );
      expect(result).toEqual(mockRun);
    });

    it('should forward MANUAL trigger correctly', async () => {
      const input: CreateRunInput = {
        jobId: 'job-2',
        trigger: 'MANUAL',
      };

      forwardRequest.mockResolvedValueOnce({ run: { runId: 'run-xyz' } });

      await client.createRun(input);

      expect(forwardRequest).toHaveBeenCalledWith(
        'POST',
        `${DEFAULT_BASE_PATH}/runs`,
        { create: input },
      );
    });
  });

  describe('reportRun', () => {
    it('should call forwardRequest with POST and wrapped report payload', async () => {
      const input: ReportRunInput = {
        runId: 'run-abc-123',
        jobId: 'job-1',
        status: 'SUCCESS',
        versionId: 'v1',
        attempt: 1,
        startedAt: '2026-06-24T10:00:00Z',
        finishedAt: '2026-06-24T10:00:05Z',
        durationMs: 5000,
        output: { result: 'ok' },
        logs: 'Job completed successfully',
        errorCode: null,
        errorMessage: null,
        errorStack: null,
      };

      forwardRequest.mockResolvedValueOnce(undefined);

      await client.reportRun(input);

      expect(forwardRequest).toHaveBeenCalledWith(
        'POST',
        `${DEFAULT_BASE_PATH}/runs`,
        { report: input },
      );
    });

    it('should call forwardRequest with FAILED status and error details', async () => {
      const input: ReportRunInput = {
        runId: 'run-fail-456',
        jobId: 'job-2',
        status: 'FAILED',
        errorCode: 'EXECUTION_ERROR',
        errorMessage: 'Unexpected token',
        errorStack: 'Error: Unexpected token\n  at eval:1:1',
      };

      forwardRequest.mockResolvedValueOnce(undefined);

      await client.reportRun(input);

      expect(forwardRequest).toHaveBeenCalledWith(
        'POST',
        `${DEFAULT_BASE_PATH}/runs`,
        { report: input },
      );
    });
  });

  describe('custom basePath via config', () => {
    it('should use ATLAS_SJOB_RUNTIME_CALLBACK_PATH when provided', async () => {
      const customPath = '/api/atlas/custom/sjob-path';
      const customForwardRequest = jest.fn();

      const module: TestingModule = await Test.createTestingModule({
        providers: [
          ScheduledJobsRuntimeClient,
          {
            provide: AtlasProxyService,
            useValue: { forwardRequest: customForwardRequest },
          },
          {
            provide: ConfigService,
            useValue: {
              get: jest.fn().mockReturnValue(customPath),
            },
          },
        ],
      }).compile();

      const customClient = module.get<ScheduledJobsRuntimeClient>(
        ScheduledJobsRuntimeClient,
      );

      customForwardRequest.mockResolvedValueOnce({
        jobId: 'job-1',
        versionId: null,
        code: '',
        env: {},
        fetchAllowlist: [],
        limits: { timeoutMs: 5000, memoryMb: 64 },
        overlapPolicy: 'ALLOW',
        maxRetries: 0,
        input: null,
      });

      await customClient.getBundle('job-1');

      expect(customForwardRequest).toHaveBeenCalledWith(
        'GET',
        `${customPath}/bundle/job-1`,
      );
    });
  });
});
