// Mock ENV
(global as any).ENV = { API_URL: 'https://api.adalong.com' };

import { buildRequestPayload, trackOrder } from '../order';
import Api from '../../../services/api';
import { AnalyticsConsent } from '../../../services/analyticsConsent';
import { Identity } from '../../../services/identity';

// Mock the services
jest.mock('../../../services/api');
jest.mock('../../../services/analyticsConsent');
jest.mock('../../../services/identity');

const mockApi = Api as jest.Mocked<typeof Api>;
const mockAnalyticsConsent = AnalyticsConsent as jest.Mocked<typeof AnalyticsConsent>;
const mockIdentity = Identity as jest.Mocked<typeof Identity>;

describe('buildRequestPayload', () => {
  beforeEach(() => {
    jest.clearAllMocks();
  });

  it('should build payload with minimal required fields', () => {
    const input = {
      orderId: 'order123',
      revenue: 100,
      currency: 'usd',
    };

    const result = buildRequestPayload(input);

    expect(result).toEqual({
      order_id: 'order123',
      revenue: 100,
      currency: 'USD',
      page_location: 'http://localhost/',
    });
  });

  it('should build payload with items', () => {
    const input = {
      orderId: 'order123',
      revenue: 100,
      currency: 'usd',
      items: [
        { productId: 'prod1', quantity: 2, price: 50 },
        { product_id: 'prod2', quantity: 1, price: 25 },
      ],
    };

    const result = buildRequestPayload(input);

    expect(result).toEqual({
      order_id: 'order123',
      revenue: 100,
      currency: 'USD',
      items: [
        { productId: 'prod1', quantity: 2, price: 50 },
        { productId: 'prod2', quantity: 1, price: 25 },
      ],
      page_location: 'http://localhost/',
    });
  });

  it('should build payload with identity when consent is given', () => {
    mockAnalyticsConsent.has.mockReturnValue(true);
    mockIdentity.getIdentity.mockReturnValue({
      visitorId: 'visitor123',
      sessionId: 'session123',
    });
    mockIdentity.touchSession.mockImplementation(() => {});

    const input = {
      orderId: 'order123',
      revenue: 100,
      currency: 'usd',
    };

    const result = buildRequestPayload(input);

    expect(result).toEqual({
      order_id: 'order123',
      revenue: 100,
      currency: 'USD',
      visitor_id: 'visitor123',
      session_id: 'session123',
      page_location: 'http://localhost/',
    });
  });

  it('should use custom pageLocation and pageReferrer', () => {
    Object.defineProperty(document, 'referrer', {
      value: 'http://referrer.com',
      writable: true,
    });

    const input = {
      orderId: 'order123',
      revenue: 100,
      currency: 'usd',
      pageLocation: 'http://custom.com',
      pageReferrer: 'http://customref.com',
    };

    const result = buildRequestPayload(input);

    expect(result.page_location).toBe('http://custom.com');
    expect(result.page_referrer).toBe('http://customref.com');
  });

  it('should round revenue to 2 decimals', () => {
    const input = {
      orderId: 'order123',
      revenue: 100.123,
      currency: 'usd',
    };

    const result = buildRequestPayload(input);

    expect(result.revenue).toBe(100.12);
  });

  it('should sanitize and truncate strings', () => {
    const longString = 'a'.repeat(200);
    const input = {
      orderId: longString,
      revenue: 100,
      currency: 'usd',
    };

    const result = buildRequestPayload(input);

    expect(result.order_id).toBe('a'.repeat(128));
  });

  it('should throw error for missing orderId', () => {
    const input = {
      revenue: 100,
      currency: 'usd',
    } as any;

    expect(() => buildRequestPayload(input)).toThrow('trackOrder: orderId is required');
  });

  it('should throw error for invalid revenue', () => {
    const input = {
      orderId: 'order123',
      revenue: 'invalid',
      currency: 'usd',
    } as any;

    expect(() => buildRequestPayload(input)).toThrow('trackOrder: revenue must be a number');
  });

  it('should throw error for missing currency', () => {
    const input = {
      orderId: 'order123',
      revenue: 100,
    } as any;

    expect(() => buildRequestPayload(input)).toThrow('trackOrder: currency is required');
  });

  it('should filter out invalid items', () => {
    const input = {
      orderId: 'order123',
      revenue: 100,
      currency: 'usd',
      items: [
        { productId: 'prod1', quantity: 2, price: 50 },
        { productId: '', quantity: 1, price: 25 }, // invalid
        { quantity: 1, price: 25 }, // no productId
      ],
    };

    const result = buildRequestPayload(input);

    expect(result.items).toEqual([
      { productId: 'prod1', quantity: 2, price: 50 },
    ]);
  });

  it('should handle products array instead of items', () => {
    const input = {
      orderId: 'order123',
      revenue: 100,
      currency: 'usd',
      products: [
        { productId: 'prod1', quantity: 2, price: 50 },
      ],
    };

    const result = buildRequestPayload(input);

    expect(result.items).toEqual([
      { productId: 'prod1', quantity: 2, price: 50 },
    ]);
  });

  it('should limit items to MAX_ITEMS', () => {
    const items = Array.from({ length: 60 }, (_, i) => ({
      productId: `prod${i}`,
      quantity: 1,
      price: 10,
    }));

    const input = {
      orderId: 'order123',
      revenue: 100,
      currency: 'usd',
      items,
    };

    const result = buildRequestPayload(input);

    expect(result.items).toHaveLength(50);
  });
});

describe('trackOrder', () => {
  beforeEach(() => {
    jest.clearAllMocks();
    mockApi.trackOrder.mockResolvedValue(undefined);
  });

  it('should return success on successful API call', async () => {
    const input = {
      orderId: 'order123',
      revenue: 100,
      currency: 'usd',
    };

    const result = await trackOrder(input);

    expect(result.success).toBe(true);
    expect(mockApi.trackOrder).toHaveBeenCalledTimes(1);
  });

  it('should return error on API failure', async () => {
    mockApi.trackOrder.mockRejectedValue(new Error('API error'));

    const input = {
      orderId: 'order123',
      revenue: 100,
      currency: 'usd',
    };

    const result = await trackOrder(input);

    expect(result.success).toBe(false);
    expect(result.error).toBe('API error');
  });

  it('should return error on invalid input', async () => {
    const input = {
      revenue: 100,
      currency: 'usd',
    } as any;

    const result = await trackOrder(input);

    expect(result.success).toBe(false);
    expect(result.error).toBe('trackOrder: orderId is required');
    expect(mockApi.trackOrder).not.toHaveBeenCalled();
  });
});