/**
 * Training Orchestrator Service Tests
 */

import { describe, it, expect, beforeEach, vi } from 'vitest'
import { createTrainingJob } from '../training-orchestrator'
import type { Context } from '../../types/context'
import * as k8s from '@kubernetes/client-node'

// Mock Kubernetes client
vi.mock('@kubernetes/client-node', () => ({
  KubeConfig: vi.fn().mockImplementation(() => ({
    loadFromDefault: vi.fn(),
    makeApiClient: vi.fn(),
  })),
  BatchV1Api: vi.fn(),
  CoreV1Api: vi.fn(),
}))

describe('Training Orchestrator Service', () => {
  let mockContext: Context
  let mockBatchApi: any
  let mockCoreApi: any

  beforeEach(() => {
    mockBatchApi = {
      createNamespacedJob: vi.fn(),
    }

    mockCoreApi = {
      readNamespace: vi.fn(),
      createNamespace: vi.fn(),
    }

    const mockKc = {
      loadFromDefault: vi.fn(),
      makeApiClient: vi.fn((api: any) => {
        if (api === k8s.BatchV1Api) return mockBatchApi
        if (api === k8s.CoreV1Api) return mockCoreApi
        return mockCoreApi
      }),
    }

    vi.mocked(k8s.KubeConfig).mockImplementation(() => mockKc as any)

    mockContext = {
      db: {} as any,
      user: {
        id: 'user-1',
        email: 'test@example.com',
        name: 'Test User',
        role: 'ADMIN',
      },
    } as Context
  })

  describe('createTrainingJob', () => {
    it('should create a Kubernetes job for training', async () => {
      mockCoreApi.readNamespace.mockRejectedValue({ statusCode: 404 })
      mockCoreApi.createNamespace.mockResolvedValue({})
      mockBatchApi.createNamespacedJob.mockResolvedValue({
        body: {
          metadata: {
            name: 'training-test-job-1234567890',
            namespace: 'training',
          },
        },
      })

      const result = await createTrainingJob(mockContext, {
        name: 'test-job',
        image: 'training-image:latest',
        namespace: 'training',
        resources: {
          cpu: '2000m',
          memory: '4Gi',
        },
      })

      expect(result).toBeDefined()
      expect(result.name).toBe('test-job')
      expect(result.status).toBe('PENDING')
      expect(mockBatchApi.createNamespacedJob).toHaveBeenCalled()
    })

    it('should support GPU allocation for training', async () => {
      mockCoreApi.readNamespace.mockResolvedValue({})
      mockBatchApi.createNamespacedJob.mockResolvedValue({
        body: {
          metadata: { name: 'training-test', namespace: 'training' },
        },
      })

      await createTrainingJob(mockContext, {
        name: 'test-job',
        image: 'training-image:latest',
        resources: {
          gpu: 2,
        },
      })

      const jobCall = mockBatchApi.createNamespacedJob.mock.calls[0]
      const job = jobCall[1]
      expect(job.spec.template.spec.containers[0].resources.limits['nvidia.com/gpu']).toBe('2')
    })

    it('should set TTL for job cleanup', async () => {
      mockCoreApi.readNamespace.mockResolvedValue({})
      mockBatchApi.createNamespacedJob.mockResolvedValue({
        body: {
          metadata: { name: 'training-test', namespace: 'training' },
        },
      })

      await createTrainingJob(mockContext, {
        name: 'test-job',
        image: 'training-image:latest',
        timeout: 7200, // 2 hours
      })

      const jobCall = mockBatchApi.createNamespacedJob.mock.calls[0]
      const job = jobCall[1]
      expect(job.spec.ttlSecondsAfterFinished).toBe(7200)
    })
  })
})

