import { describe, expect, it } from 'bun:test';
import { createJobHandler } from '../src/utils/create-job-handler';

describe('createJobHandler', () => {
  interface TestPayload {
    userId: string;
    action: string;
    data?: any;
  }

  interface TestResult {
    success: boolean;
    processedBy: string;
    timestamp: Date;
  }

  it('should create a type-safe job handler function', () => {
    const handler = createJobHandler<TestPayload, TestResult>(async job => {
      // Job should have correct payload type
      const { userId, action, data } = job.data;

      return {
        success: true,
        processedBy: userId,
        timestamp: new Date(),
      };
    });

    expect(typeof handler).toBe('function');
  });

  it('should execute handler with job data', async () => {
    const testPayload: TestPayload = {
      userId: 'user-123',
      action: 'process',
      data: { value: 42 },
    };

    const handler = createJobHandler<TestPayload, TestResult>(async job => {
      expect(job.data).toEqual(testPayload);
      expect(job.id).toBe('job-123');
      expect(job.name).toBe('test-job');

      return {
        success: true,
        processedBy: job.data.userId,
        timestamp: new Date(),
      };
    });

    // Create a mock job
    const mockJob = {
      id: 'job-123',
      name: 'test-job',
      data: testPayload,
      opts: {},
      progress: () => {},
      log: () => {},
      updateProgress: async () => {},
    };

    const result = await handler(mockJob as any);

    expect(result.success).toBe(true);
    expect(result.processedBy).toBe('user-123');
    expect(result.timestamp).toBeInstanceOf(Date);
  });

  it('should handle errors in handler', async () => {
    const handler = createJobHandler<TestPayload, TestResult>(async job => {
      if (job.data.action === 'fail') {
        throw new Error('Handler error');
      }

      return {
        success: true,
        processedBy: job.data.userId,
        timestamp: new Date(),
      };
    });

    const mockJob = {
      id: 'job-456',
      name: 'test-job',
      data: {
        userId: 'user-456',
        action: 'fail',
      },
      opts: {},
      progress: () => {},
      log: () => {},
      updateProgress: async () => {},
    };

    await expect(handler(mockJob as any)).rejects.toThrow('Handler error');
  });

  it('should support async operations', async () => {
    const handler = createJobHandler<TestPayload, TestResult>(async job => {
      // Simulate async operation
      await new Promise(resolve => setTimeout(resolve, 10));

      return {
        success: true,
        processedBy: job.data.userId,
        timestamp: new Date(),
      };
    });

    const mockJob = {
      id: 'job-789',
      name: 'async-job',
      data: {
        userId: 'user-789',
        action: 'async-process',
      },
      opts: {},
      progress: () => {},
      log: () => {},
      updateProgress: async () => {},
    };

    const startTime = Date.now();
    const result = await handler(mockJob as any);
    const endTime = Date.now();

    expect(result.success).toBe(true);
    expect(endTime - startTime).toBeGreaterThanOrEqual(10);
  });

  it('should maintain type safety for complex payloads', () => {
    interface ComplexPayload {
      user: {
        id: string;
        name: string;
        roles: string[];
      };
      request: {
        type: 'CREATE' | 'UPDATE' | 'DELETE';
        resource: string;
        data: Record<string, any>;
      };
      metadata: {
        timestamp: Date;
        source: string;
        version: number;
      };
    }

    interface ComplexResult {
      status: 'success' | 'failure';
      changes: Array<{
        field: string;
        oldValue: any;
        newValue: any;
      }>;
      audit: {
        performedBy: string;
        performedAt: Date;
        duration: number;
      };
    }

    const handler = createJobHandler<ComplexPayload, ComplexResult>(async job => {
      const startTime = Date.now();

      // Type-safe access to nested properties
      const userId = job.data.user.id;
      const requestType = job.data.request.type;
      const version = job.data.metadata.version;

      return {
        status: 'success',
        changes: [
          {
            field: 'resource',
            oldValue: null,
            newValue: job.data.request.resource,
          },
        ],
        audit: {
          performedBy: userId,
          performedAt: new Date(),
          duration: Date.now() - startTime,
        },
      };
    });

    expect(typeof handler).toBe('function');
  });

  it('should work with job progress reporting', async () => {
    let progressValue = 0;

    const handler = createJobHandler<TestPayload, TestResult>(async job => {
      // Report progress
      await job.updateProgress(25);
      progressValue = 25;

      await new Promise(resolve => setTimeout(resolve, 10));

      await job.updateProgress(50);
      progressValue = 50;

      await new Promise(resolve => setTimeout(resolve, 10));

      await job.updateProgress(100);
      progressValue = 100;

      return {
        success: true,
        processedBy: job.data.userId,
        timestamp: new Date(),
      };
    });

    const mockJob = {
      id: 'job-progress',
      name: 'progress-job',
      data: {
        userId: 'user-progress',
        action: 'long-process',
      },
      opts: {},
      progress: () => progressValue,
      log: () => {},
      updateProgress: async (value: number) => {
        progressValue = value;
      },
    };

    const result = await handler(mockJob as any);

    expect(result.success).toBe(true);
    expect(progressValue).toBe(100);
  });
});
