import * as crypto from 'crypto';
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { ZalopayService } from '../services/zalopay.service';

describe('ZalopayService', () => {
  let service: ZalopayService;
  const key2 = 'test-zalopay-key2-for-callback!!';

  beforeEach(() => {
    vi.stubEnv('ZALOPAY_APP_ID', '2553');
    vi.stubEnv('ZALOPAY_KEY1', 'test-zalopay-key1-for-signing!!a');
    vi.stubEnv('ZALOPAY_KEY2', key2);
    service = new ZalopayService();
  });

  function buildCallbackData(
    dataPayload: Record<string, unknown> = {},
    tamperMac = false,
  ): Record<string, string> {
    const payload = {
      app_id: 2553,
      app_trans_id: '260408_order-123',
      zp_trans_id: 'ZLP_TX_456',
      ...dataPayload,
    };
    const dataStr = JSON.stringify(payload);
    let mac = crypto
      .createHmac('sha256', key2)
      .update(dataStr)
      .digest('hex');

    if (tamperMac) {
      mac = 'a'.repeat(mac.length);
    }

    return { data: dataStr, mac };
  }

  it('should verify a valid callback with timingSafeEqual', () => {
    const data = buildCallbackData();
    const result = service.verifyCallback(data);

    expect(result.isValid).toBe(true);
    expect(result.isSuccess).toBe(true);
    expect(result.orderId).toBe('260408_order-123');
    expect(result.providerTxId).toBe('ZLP_TX_456');
  });

  it('should reject an invalid MAC', () => {
    const data = buildCallbackData({}, true);
    const result = service.verifyCallback(data);

    expect(result.isValid).toBe(false);
    expect(result.isSuccess).toBe(false);
  });

  it('should reject an empty MAC', () => {
    const data = buildCallbackData();
    data['mac'] = '';

    const result = service.verifyCallback(data);
    expect(result.isValid).toBe(false);
  });

  it('should reject a MAC with wrong length', () => {
    const data = buildCallbackData();
    data['mac'] = 'deadbeef'; // too short

    const result = service.verifyCallback(data);
    expect(result.isValid).toBe(false);
  });

  it('should handle invalid JSON in data field gracefully', () => {
    const mac = crypto
      .createHmac('sha256', key2)
      .update('not-json')
      .digest('hex');

    const result = service.verifyCallback({ data: 'not-json', mac });
    // timingSafeEqual passes but JSON.parse fails
    expect(result.isValid).toBe(false);
    expect(result.isSuccess).toBe(false);
  });
});
