import dayjs from '../../src/libs/dayjs';
import {
  getBillingThreshold,
  getCheckoutAmount,
  getCheckoutMode,
  getRecurringPeriod,
  getSubscriptionCreateSetup,
  getCheckoutSessionSubscriptionIds,
  getOneTimeLineItems,
  getRecurringLineItems,
  mergeSubscriptionDataFromLineItems,
  getFastCheckoutAmount,
  createPaymentBeneficiaries,
  getSubscriptionLineItems,
} from '../../src/libs/session';
import { getPriceCurrencyOptions, getPriceUintAmountByCurrency } from '../../src/libs/price';
import type { TLineItemExpanded } from '../../src/store/models';
import type { PaymentBeneficiary } from '../../src/store/models/types';
import { SubscriptionItem } from '../../src/store/models';

describe('getCheckoutMode', () => {
  it('should return "setup" when items array is empty', () => {
    const result = getCheckoutMode([]);
    expect(result).toBe('setup');
  });

  it('should return "setup" when all items are metered', () => {
    const items = [
      { price: { type: 'recurring', recurring: { usage_type: 'metered' } } },
      { price: { type: 'recurring', recurring: { usage_type: 'metered' } } },
    ];
    const result = getCheckoutMode(items as TLineItemExpanded[]);
    expect(result).toBe('setup');
  });

  it('should return "subscription" when some items are recurring', () => {
    const items = [
      { price: { type: 'recurring', recurring: { usage_type: 'metered' } } },
      { price: { type: 'recurring' } },
    ];
    const result = getCheckoutMode(items as TLineItemExpanded[]);
    expect(result).toBe('subscription');
  });

  it('should return "payment" when none of the above conditions are met', () => {
    const items = [{ price: { type: 'one_time' } }, { price: { type: 'one_time' } }];
    const result = getCheckoutMode(items as TLineItemExpanded[]);
    expect(result).toBe('payment');
  });
});

describe('getPriceCurrencyOptions', () => {
  it('should return currency_options when it is an array', () => {
    const price = {
      currency_options: [
        { currency_id: 'usd', unit_amount: 1000, tiers: null, custom_unit_amount: null },
        { currency_id: 'eur', unit_amount: 900, tiers: null, custom_unit_amount: null },
      ],
      currency_id: 'usd',
      unit_amount: 1000,
    };
    const result = getPriceCurrencyOptions(price as any);
    expect(result).toEqual(price.currency_options);
  });

  it('should return an array with a single object when currency_options is not an array', () => {
    const price = {
      currency_id: 'usd',
      unit_amount: 1000,
    };
    const result = getPriceCurrencyOptions(price as any);
    expect(result).toEqual([{ currency_id: 'usd', unit_amount: 1000, tiers: null, custom_unit_amount: null }]);
  });
});

describe('getPriceUintAmountByCurrency', () => {
  it('should return the unit_amount of the matching currency_id in currency_options', () => {
    const price = {
      currency_options: [
        { currency_id: 'usd', unit_amount: 1000 },
        { currency_id: 'eur', unit_amount: 900 },
      ],
      currency_id: 'usd',
      unit_amount: 1000,
    };
    const result = getPriceUintAmountByCurrency(price as any, 'eur');
    expect(result).toBe(900);
  });

  it('should throw when the currency_id is not found in currency_options', () => {
    const price = {
      currency_options: [
        { currency_id: 'usd', unit_amount: 1000 },
        { currency_id: 'eur', unit_amount: 900 },
      ],
      currency_id: 'usd',
      unit_amount: 1000,
    };
    expect(() => getPriceUintAmountByCurrency(price as any, 'gbp')).toThrow();
  });

  it('should throw when currency_options is not an array', () => {
    const price = {
      currency_id: 'usd',
      unit_amount: 1000,
    };
    expect(() => getPriceUintAmountByCurrency(price as any, 'eur')).toThrow();
  });
});

describe('getRecurringPeriod', () => {
  it('should return the correct period in milliseconds when interval is "hour"', () => {
    const recurring = { interval: 'hour', interval_count: '1' };
    const result = getRecurringPeriod(recurring as any);
    expect(result).toBe(60 * 60 * 1000);
  });

  it('should return the correct period in milliseconds when interval is "day"', () => {
    const recurring = { interval: 'day', interval_count: '1' };
    const result = getRecurringPeriod(recurring as any);
    expect(result).toBe(24 * 60 * 60 * 1000);
  });

  it('should return the correct period in milliseconds when interval is "week"', () => {
    const recurring = { interval: 'week', interval_count: '1' };
    const result = getRecurringPeriod(recurring as any);
    expect(result).toBe(7 * 24 * 60 * 60 * 1000);
  });

  it('should return the correct period in milliseconds when interval is "month"', () => {
    const recurring = { interval: 'month', interval_count: '1' };
    const result = getRecurringPeriod(recurring as any);
    expect(result).toBe(30 * 24 * 60 * 60 * 1000);
  });

  it('should return the correct period in milliseconds when interval is "year"', () => {
    const recurring = { interval: 'year', interval_count: '1' };
    const result = getRecurringPeriod(recurring as any);
    expect(result).toBe(365 * 24 * 60 * 60 * 1000);
  });

  it('should return 0 when interval is an invalid value', () => {
    const recurring = { interval: 'invalid', interval_count: '1' };
    const result = getRecurringPeriod(recurring as any);
    expect(result).toBe(0);
  });
});

describe('getBillingThreshold', () => {
  afterEach(() => {
    delete process.env.PAYMENT_BILLING_THRESHOLD;
  });

  it('should return the billing_threshold_amount from the config if it is a positive number', () => {
    const config = { billing_threshold_amount: '100' };
    const result = getBillingThreshold(config);
    expect(result).toBe(100);
  });

  it('should return the PAYMENT_BILLING_THRESHOLD from the environment variables if the config does not contain a positive billing_threshold_amount', () => {
    process.env.PAYMENT_BILLING_THRESHOLD = '200';
    const config = { billing_threshold_amount: '-100' };
    const result = getBillingThreshold(config);
    expect(result).toBe(200);
  });

  it('should return 0 if neither the config nor the environment variables contain a positive billing_threshold_amount', () => {
    const config = { billing_threshold_amount: '-100' };
    const result = getBillingThreshold(config);
    expect(result).toBe(0);
  });

  it('should return 0 if the config is not provided and the environment variables do not contain a positive billing_threshold_amount', () => {
    const result = getBillingThreshold();
    expect(result).toBe(0);
  });
});

describe('await getCheckoutAmount', () => {
  it('should calculate the total amount correctly for recurring items', async () => {
    const items = [
      {
        price: { type: 'recurring', recurring: { usage_type: 'licensed' }, currency_id: 'usd', unit_amount: '10' },
        quantity: 2,
      },
      {
        price: { type: 'recurring', recurring: { usage_type: 'metered' }, currency_id: 'usd', unit_amount: '10' },
        quantity: 3,
      },
    ];
    const currencyId = 'usd';
    const result = await getCheckoutAmount(items as TLineItemExpanded[], currencyId);
    expect(result.total).toBe('20'); // Update the expected value based on your calculation
  });

  it('should calculate the total amount correctly for non-recurring items', async () => {
    const items = [
      { price: { type: 'one_time', currency_id: 'usd', unit_amount: '10' }, quantity: 2 },
      { price: { type: 'one_time', currency_id: 'usd', unit_amount: '20' }, quantity: 3 },
    ];
    const currencyId = 'usd';
    const result = await getCheckoutAmount(items as TLineItemExpanded[], currencyId);
    expect(result.total).toBe('80');
  });

  it('should calculate the total amount correctly when trialing is true', async () => {
    const items = [
      {
        price: { type: 'recurring', recurring: { usage_type: 'licensed' }, currency_id: 'usd', unit_amount: '10' },
        quantity: 2,
      },
      {
        price: { type: 'recurring', recurring: { usage_type: 'metered' }, currency_id: 'usd', unit_amount: '20' },
        quantity: 3,
      },
    ];
    const currencyId = 'usd';
    const result = await getCheckoutAmount(items as TLineItemExpanded[], currencyId, true);
    expect(result.total).toBe('0');
  });

  it('should throw when when custom_unit_amount is provided with other item', async () => {
    const items = [
      {
        price: { type: 'recurring', recurring: { usage_type: 'licensed' }, currency_id: 'usd', unit_amount: '10' },
        quantity: 2,
      },
      {
        price: {
          type: 'one_time',
          unit_amount: '0',
          currency_id: 'usd',
          custom_unit_amount: { preset: '10' },
        },
        quantity: 1,
      },
    ];
    const currencyId = 'usd';
    await expect(getCheckoutAmount(items as any[], currencyId)).rejects.toThrow(
      'Multiple items with custom unit amount are not supported'
    );
  });

  it('should calculate the total amount correctly when custom_unit_amount.preset is provided', async () => {
    const items = [
      {
        price: {
          type: 'one_time',
          unit_amount: '0',
          currency_id: 'usd',
          custom_unit_amount: { preset: '10' },
        },
        quantity: 1,
      },
    ];
    const currencyId = 'usd';
    const result = await getCheckoutAmount(items as TLineItemExpanded[], currencyId);
    expect(result.total).toBe('10');
  });

  it('should calculate the total amount correctly when custom_unit_amount.presets is provided', async () => {
    const items = [
      {
        price: {
          type: 'one_time',
          unit_amount: '0',
          currency_id: 'usd',
          custom_unit_amount: { presets: ['10'] },
        },
        quantity: 2,
      },
    ];
    const currencyId = 'usd';
    const result = await getCheckoutAmount(items as TLineItemExpanded[], currencyId);
    expect(result.total).toBe('20');
  });
});

describe('getSubscriptionCreateSetup', () => {
  const currencies = [
    {
      currency_id: 'usd',
      unit_amount: '1',
    },
  ];

  it('should calculate setup for recurring licensed price type', () => {
    const items = [
      {
        price: { type: 'one_time', currency_options: currencies },
        quantity: 1,
      },
      {
        price: {
          type: 'recurring',
          currency_options: currencies,
          recurring: { interval: 'day', interval_count: '1', usage_type: 'licensed' },
        },
        quantity: 2,
      },
    ];
    const result = getSubscriptionCreateSetup(items as any, 'usd');
    expect(result.amount.setup).toBe('3');
  });

  it('should not calculate setup for recurring metered price type', () => {
    const items = [
      {
        price: { type: 'one_time', currency_options: currencies },
        quantity: 1,
      },
      {
        price: {
          type: 'recurring',
          currency_options: currencies,
          recurring: { interval: 'day', interval_count: '1', usage_type: 'metered' },
        },
        quantity: 2,
      },
    ];
    const result = getSubscriptionCreateSetup(items as any, 'usd');
    expect(result.amount.setup).toBe('1');
  });

  it('should calculate cycle duration for recurring price type', () => {
    const items = [
      {
        price: { type: 'recurring', currency_options: currencies, recurring: { interval: 'day', interval_count: '1' } },
        quantity: 2,
      },
    ];
    const result = getSubscriptionCreateSetup(items as any, 'usd');
    expect(result.cycle.duration).toBe(24 * 60 * 60 * 1000);
  });

  it('should calculate trial period when only trialInDays is provided', () => {
    const items = [
      {
        price: { type: 'recurring', currency_options: currencies, recurring: { interval: 'day', interval_count: '1' } },
        quantity: 2,
      },
    ];
    const result = getSubscriptionCreateSetup(items as any, 'usd', 7);
    const now = dayjs().unix();
    expect(result.trial.start).toBe(now);
    expect(result.trial.end).toBe(
      dayjs()
        .add(7 * 24 * 60 * 60 * 1000, 'millisecond')
        .unix()
    );
  });

  it('should trialEnd overwrite trialInDays', () => {
    const items = [
      {
        price: { type: 'recurring', currency_options: currencies, recurring: { interval: 'day', interval_count: '1' } },
        quantity: 2,
      },
    ];
    const result = getSubscriptionCreateSetup(items as any, 'usd', 1, dayjs().add(7, 'day').unix());
    const now = dayjs().unix();
    expect(result.trial.start).toBe(now);
    expect(result.trial.end).toBe(
      dayjs()
        .add(7 * 24 * 60 * 60 * 1000, 'millisecond')
        .unix()
    );
  });

  it('should calculate trial period when only trialEnd is provided', () => {
    const items = [
      {
        price: { type: 'recurring', currency_options: currencies, recurring: { interval: 'day', interval_count: '1' } },
        quantity: 2,
      },
    ];
    const result = getSubscriptionCreateSetup(items as any, 'usd', 0, dayjs().add(7, 'day').unix());
    const now = dayjs().unix();
    expect(result.trial.start).toBe(now);
    expect(result.trial.end).toBe(
      dayjs()
        .add(7 * 24 * 60 * 60 * 1000, 'millisecond')
        .unix()
    );
  });

  describe('subscription authorization slippage protection', () => {
    it('should NOT apply slippage for static pricing (backward compatibility)', () => {
      const items = [
        {
          price: {
            type: 'recurring',
            currency_options: [{ currency_id: 'usd', unit_amount: '1000' }],
            recurring: { interval: 'month', interval_count: '1', usage_type: 'licensed' },
            pricing_type: 'fixed', // Static pricing
          },
          quantity: 1,
        },
      ];
      const result = getSubscriptionCreateSetup(items as any, 'usd');
      // Base: 1000, No slippage for static pricing
      expect(result.amount.setup).toBe('1000');
    });

    it('should apply 0.5% slippage protection for dynamic pricing', () => {
      const items = [
        {
          price: {
            type: 'recurring',
            currency_options: [{ currency_id: 'usd', unit_amount: '1000' }],
            recurring: { interval: 'month', interval_count: '1', usage_type: 'licensed' },
            pricing_type: 'dynamic', // Dynamic pricing
          },
          quantity: 1,
        },
      ];
      const result = getSubscriptionCreateSetup(items as any, 'usd');
      // Base: 1000, With 0.5% slippage: 1000 * 1.005 = 1005
      expect(result.amount.setup).toBe('1005');
    });

    it('should apply slippage correctly with multiple quantities', () => {
      const items = [
        {
          price: {
            type: 'recurring',
            currency_options: [{ currency_id: 'usd', unit_amount: '1000' }],
            recurring: { interval: 'month', interval_count: '1', usage_type: 'licensed' },
            pricing_type: 'dynamic',
          },
          quantity: 3,
        },
      ];
      const result = getSubscriptionCreateSetup(items as any, 'usd');
      // Base: 1000 * 3 = 3000, With 0.5% slippage: 3000 * 1.005 = 3015
      expect(result.amount.setup).toBe('3015');
    });

    it('should apply slippage with mixed dynamic and one-time items', () => {
      const items = [
        {
          price: {
            type: 'recurring',
            currency_options: [{ currency_id: 'usd', unit_amount: '1000' }],
            recurring: { interval: 'month', interval_count: '1', usage_type: 'licensed' },
            pricing_type: 'dynamic',
          },
          quantity: 1,
        },
        {
          price: {
            type: 'one_time',
            currency_options: [{ currency_id: 'usd', unit_amount: '500' }],
          },
          quantity: 1,
        },
      ];
      const result = getSubscriptionCreateSetup(items as any, 'usd');
      // Base: 1000 (recurring) + 500 (one-time) = 1500
      // With 0.5% slippage: 1500 * 1.005 = 1507
      expect(result.amount.setup).toBe('1507');
    });

    it('should apply slippage with mixed dynamic and static recurring items', () => {
      const items = [
        {
          price: {
            type: 'recurring',
            currency_options: [{ currency_id: 'usd', unit_amount: '1000' }],
            recurring: { interval: 'month', interval_count: '1', usage_type: 'licensed' },
            pricing_type: 'dynamic',
          },
          quantity: 1,
        },
        {
          price: {
            type: 'recurring',
            currency_options: [{ currency_id: 'usd', unit_amount: '500' }],
            recurring: { interval: 'month', interval_count: '1', usage_type: 'licensed' },
            pricing_type: 'fixed', // Static pricing
          },
          quantity: 1,
        },
      ];
      const result = getSubscriptionCreateSetup(items as any, 'usd');
      // Base: 1000 (dynamic) + 500 (static) = 1500
      // With 0.5% slippage (because at least one item is dynamic): 1500 * 1.005 = 1507
      expect(result.amount.setup).toBe('1507');
    });

    it('should NOT apply slippage when setup amount is zero', () => {
      const items = [
        {
          price: {
            type: 'recurring',
            currency_options: [{ currency_id: 'usd', unit_amount: '0' }],
            recurring: { interval: 'month', interval_count: '1', usage_type: 'metered' }, // Metered not included in setup
            pricing_type: 'dynamic',
          },
          quantity: 1,
        },
      ];
      const result = getSubscriptionCreateSetup(items as any, 'usd');
      // Base: 0 (metered), No slippage applied
      expect(result.amount.setup).toBe('0');
    });

    it('should handle large amounts with slippage correctly', () => {
      const items = [
        {
          price: {
            type: 'recurring',
            currency_options: [{ currency_id: 'usd', unit_amount: '10000000' }], // 10M
            recurring: { interval: 'year', interval_count: '1', usage_type: 'licensed' },
            pricing_type: 'dynamic',
          },
          quantity: 1,
        },
      ];
      const result = getSubscriptionCreateSetup(items as any, 'usd');
      // Base: 10,000,000, With 0.5% slippage: 10,000,000 * 1.005 = 10,050,000
      expect(result.amount.setup).toBe('10050000');
    });

    // Tests for custom slippage percentage parameter
    it('should apply custom 5% slippage for dynamic pricing', () => {
      const items = [
        {
          price: {
            type: 'recurring',
            currency_options: [{ currency_id: 'usd', unit_amount: '1000' }],
            recurring: { interval: 'month', interval_count: '1', usage_type: 'licensed' },
            pricing_type: 'dynamic',
          },
          quantity: 1,
        },
      ];
      const result = getSubscriptionCreateSetup(items as any, 'usd', 0, 0, 5);
      // Base: 1000, With 5% slippage: 1000 * 1.05 = 1050
      expect(result.amount.setup).toBe('1050');
    });

    it('should apply custom 10% slippage for dynamic pricing', () => {
      const items = [
        {
          price: {
            type: 'recurring',
            currency_options: [{ currency_id: 'usd', unit_amount: '1000' }],
            recurring: { interval: 'month', interval_count: '1', usage_type: 'licensed' },
            pricing_type: 'dynamic',
          },
          quantity: 1,
        },
      ];
      const result = getSubscriptionCreateSetup(items as any, 'usd', 0, 0, 10);
      // Base: 1000, With 10% slippage: 1000 * 1.10 = 1100
      expect(result.amount.setup).toBe('1100');
    });

    it('should apply zero slippage when slippagePercent is 0', () => {
      const items = [
        {
          price: {
            type: 'recurring',
            currency_options: [{ currency_id: 'usd', unit_amount: '1000' }],
            recurring: { interval: 'month', interval_count: '1', usage_type: 'licensed' },
            pricing_type: 'dynamic',
          },
          quantity: 1,
        },
      ];
      const result = getSubscriptionCreateSetup(items as any, 'usd', 0, 0, 0);
      // Base: 1000, With 0% slippage: 1000 * 1.00 = 1000
      expect(result.amount.setup).toBe('1000');
    });

    it('should fallback to 0.5% slippage when slippagePercent is negative', () => {
      const items = [
        {
          price: {
            type: 'recurring',
            currency_options: [{ currency_id: 'usd', unit_amount: '1000' }],
            recurring: { interval: 'month', interval_count: '1', usage_type: 'licensed' },
            pricing_type: 'dynamic',
          },
          quantity: 1,
        },
      ];
      const result = getSubscriptionCreateSetup(items as any, 'usd', 0, 0, -1);
      // Fallback to 0.5%: 1000 * 1.005 = 1005
      expect(result.amount.setup).toBe('1005');
    });

    it('should fallback to 0.5% slippage when slippagePercent is NaN', () => {
      const items = [
        {
          price: {
            type: 'recurring',
            currency_options: [{ currency_id: 'usd', unit_amount: '1000' }],
            recurring: { interval: 'month', interval_count: '1', usage_type: 'licensed' },
            pricing_type: 'dynamic',
          },
          quantity: 1,
        },
      ];
      const result = getSubscriptionCreateSetup(items as any, 'usd', 0, 0, NaN);
      // Fallback to 0.5%: 1000 * 1.005 = 1005
      expect(result.amount.setup).toBe('1005');
    });

    it('should handle fractional slippage percentages (1.25%)', () => {
      const items = [
        {
          price: {
            type: 'recurring',
            currency_options: [{ currency_id: 'usd', unit_amount: '1000' }],
            recurring: { interval: 'month', interval_count: '1', usage_type: 'licensed' },
            pricing_type: 'dynamic',
          },
          quantity: 1,
        },
      ];
      const result = getSubscriptionCreateSetup(items as any, 'usd', 0, 0, 1.25);
      // Base: 1000, With 1.25% slippage: multiplier = Math.round(1000 + 12.5) = 1013
      // 1000 * 1013 / 1000 = 1013
      expect(result.amount.setup).toBe('1013');
    });

    it('should NOT apply custom slippage for static pricing', () => {
      const items = [
        {
          price: {
            type: 'recurring',
            currency_options: [{ currency_id: 'usd', unit_amount: '1000' }],
            recurring: { interval: 'month', interval_count: '1', usage_type: 'licensed' },
            pricing_type: 'fixed',
          },
          quantity: 1,
        },
      ];
      const result = getSubscriptionCreateSetup(items as any, 'usd', 0, 0, 10);
      // Static pricing: No slippage applied regardless of parameter
      expect(result.amount.setup).toBe('1000');
    });

    // Tests for SlippageOptions object with minAcceptableRate
    it('should accept SlippageOptions object with percent', () => {
      const items = [
        {
          price: {
            type: 'recurring',
            currency_options: [{ currency_id: 'usd', unit_amount: '1000' }],
            recurring: { interval: 'month', interval_count: '1', usage_type: 'licensed' },
            pricing_type: 'dynamic',
          },
          quantity: 1,
        },
      ];
      const result = getSubscriptionCreateSetup(items as any, 'usd', 0, 0, { percent: 5 });
      // Base: 1000, With 5% slippage: 1000 * 1.05 = 1050
      expect(result.amount.setup).toBe('1050');
    });

    it('should calculate authorization using minAcceptableRate when provided', () => {
      const items = [
        {
          price: {
            type: 'recurring',
            currency_options: [{ currency_id: 'usd', unit_amount: '1000' }],
            recurring: { interval: 'month', interval_count: '1', usage_type: 'licensed' },
            pricing_type: 'dynamic',
            base_amount: '100', // $100 USD
          },
          quantity: 1,
        },
      ];
      // min_acceptable_rate = 0.5 means 1 token = $0.5 USD
      // For $100 USD, we need: 100 / 0.5 = 200 tokens
      const result = getSubscriptionCreateSetup(items as any, 'usd', 0, 0, {
        percent: 5, // Should be ignored when minAcceptableRate is provided
        minAcceptableRate: '0.5',
        currencyDecimal: 0, // Simplified for testing
      });
      expect(result.amount.setup).toBe('200');
    });

    it('should use ceiling division for minAcceptableRate calculation', () => {
      const items = [
        {
          price: {
            type: 'recurring',
            currency_options: [{ currency_id: 'usd', unit_amount: '1000' }],
            recurring: { interval: 'month', interval_count: '1', usage_type: 'licensed' },
            pricing_type: 'dynamic',
            base_amount: '100', // $100 USD
          },
          quantity: 1,
        },
      ];
      // min_acceptable_rate = 0.3 means 1 token = $0.3 USD
      // For $100 USD, we need: ceil(100 / 0.3) = ceil(333.33) = 334 tokens
      const result = getSubscriptionCreateSetup(items as any, 'usd', 0, 0, {
        minAcceptableRate: '0.3',
        currencyDecimal: 0,
      });
      expect(result.amount.setup).toBe('334');
    });

    it('should handle multiple dynamic pricing items with minAcceptableRate', () => {
      const items = [
        {
          price: {
            type: 'recurring',
            currency_options: [{ currency_id: 'usd', unit_amount: '500' }],
            recurring: { interval: 'month', interval_count: '1', usage_type: 'licensed' },
            pricing_type: 'dynamic',
            base_amount: '50', // $50 USD
          },
          quantity: 2, // Total: $100 USD
        },
        {
          price: {
            type: 'recurring',
            currency_options: [{ currency_id: 'usd', unit_amount: '250' }],
            recurring: { interval: 'month', interval_count: '1', usage_type: 'licensed' },
            pricing_type: 'dynamic',
            base_amount: '25', // $25 USD
          },
          quantity: 2, // Total: $50 USD
        },
      ];
      // Total base_amount: $100 + $50 = $150 USD
      // min_acceptable_rate = 0.5 means 1 token = $0.5 USD
      // For $150 USD, we need: 150 / 0.5 = 300 tokens
      const result = getSubscriptionCreateSetup(items as any, 'usd', 0, 0, {
        minAcceptableRate: '0.5',
        currencyDecimal: 0,
      });
      expect(result.amount.setup).toBe('300');
    });

    it('should fallback to percent when minAcceptableRate is missing', () => {
      const items = [
        {
          price: {
            type: 'recurring',
            currency_options: [{ currency_id: 'usd', unit_amount: '1000' }],
            recurring: { interval: 'month', interval_count: '1', usage_type: 'licensed' },
            pricing_type: 'dynamic',
            base_amount: '100',
          },
          quantity: 1,
        },
      ];
      const result = getSubscriptionCreateSetup(items as any, 'usd', 0, 0, {
        percent: 10,
        // minAcceptableRate is undefined
        currencyDecimal: 0,
      });
      // Fallback to percent: 1000 * 1.10 = 1100
      expect(result.amount.setup).toBe('1100');
    });

    it('should fallback to percent when currencyDecimal is missing', () => {
      const items = [
        {
          price: {
            type: 'recurring',
            currency_options: [{ currency_id: 'usd', unit_amount: '1000' }],
            recurring: { interval: 'month', interval_count: '1', usage_type: 'licensed' },
            pricing_type: 'dynamic',
            base_amount: '100',
          },
          quantity: 1,
        },
      ];
      const result = getSubscriptionCreateSetup(items as any, 'usd', 0, 0, {
        percent: 5,
        minAcceptableRate: '0.5',
        // currencyDecimal is undefined
      });
      // Fallback to percent: 1000 * 1.05 = 1050
      expect(result.amount.setup).toBe('1050');
    });

    it('should fallback to percent when minAcceptableRate is zero', () => {
      const items = [
        {
          price: {
            type: 'recurring',
            currency_options: [{ currency_id: 'usd', unit_amount: '1000' }],
            recurring: { interval: 'month', interval_count: '1', usage_type: 'licensed' },
            pricing_type: 'dynamic',
            base_amount: '100',
          },
          quantity: 1,
        },
      ];
      const result = getSubscriptionCreateSetup(items as any, 'usd', 0, 0, {
        percent: 5,
        minAcceptableRate: '0',
        currencyDecimal: 0,
      });
      // Fallback to percent: 1000 * 1.05 = 1050
      expect(result.amount.setup).toBe('1050');
    });

    it('should fallback to default 0.5% when SlippageOptions is empty object', () => {
      const items = [
        {
          price: {
            type: 'recurring',
            currency_options: [{ currency_id: 'usd', unit_amount: '1000' }],
            recurring: { interval: 'month', interval_count: '1', usage_type: 'licensed' },
            pricing_type: 'dynamic',
          },
          quantity: 1,
        },
      ];
      const result = getSubscriptionCreateSetup(items as any, 'usd', 0, 0, {});
      // Fallback to default 0.5%: 1000 * 1.005 = 1005
      expect(result.amount.setup).toBe('1005');
    });

    it('should handle minAcceptableRate with higher precision currencyDecimal', () => {
      const items = [
        {
          price: {
            type: 'recurring',
            currency_options: [{ currency_id: 'eth', unit_amount: '100000000000000000' }], // 0.1 ETH in wei
            recurring: { interval: 'month', interval_count: '1', usage_type: 'licensed' },
            pricing_type: 'dynamic',
            base_amount: '100', // $100 USD
          },
          quantity: 1,
        },
      ];
      // min_acceptable_rate = 1000 means 1 token (wei) = $1000 USD
      // But we have 18 decimals, so 1 ETH = $1000 * 10^18 / 10^18 = $1000
      // For $100 USD with currencyDecimal=18:
      // authorization = ceil(100 * 10^8 * 10^18 / (1000 * 10^8))
      // = ceil(10^28 / 10^11) = ceil(10^17) = 100000000000000000 (0.1 ETH)
      const result = getSubscriptionCreateSetup(items as any, 'eth', 0, 0, {
        minAcceptableRate: '1000',
        currencyDecimal: 18,
      });
      expect(result.amount.setup).toBe('100000000000000000');
    });
  });
});

describe('getCheckoutSessionSubscriptionIds', () => {
  it('should return array with subscription_id when enable_subscription_grouping is false', () => {
    const checkoutSession = {
      subscription_id: 'sub_123',
      enable_subscription_grouping: false,
      subscription_groups: { price_1: 'sub_456', price_2: 'sub_789' },
    };
    const result = getCheckoutSessionSubscriptionIds(checkoutSession as any);
    expect(result).toEqual(['sub_123']);
  });

  it('should return array of subscription_groups values when enable_subscription_grouping is true', () => {
    const checkoutSession = {
      subscription_id: 'sub_123',
      enable_subscription_grouping: true,
      subscription_groups: { price_1: 'sub_456', price_2: 'sub_789' },
    };
    const result = getCheckoutSessionSubscriptionIds(checkoutSession as any);
    expect(result).toEqual(['sub_456', 'sub_789']);
  });

  it('should filter out empty subscription ids', () => {
    const checkoutSession = {
      enable_subscription_grouping: true,
      subscription_groups: { price_1: 'sub_456', price_2: '', price_3: null },
    };
    const result = getCheckoutSessionSubscriptionIds(checkoutSession as any);
    expect(result).toEqual(['sub_456']);
  });

  it('should return empty array when no subscription ids exist', () => {
    const checkoutSession = {
      enable_subscription_grouping: true,
      subscription_groups: {},
    };
    const result = getCheckoutSessionSubscriptionIds(checkoutSession as any);
    expect(result).toEqual([]);
  });
});

describe('getOneTimeLineItems', () => {
  it('should return only one-time line items', () => {
    const lineItems = [
      { price: { type: 'one_time' } },
      { price: { type: 'recurring' } },
      { price: { type: 'one_time' } },
    ];
    const result = getOneTimeLineItems(lineItems as TLineItemExpanded[]);
    expect(result).toHaveLength(2);
    // @ts-ignore
    expect(result[0].price && result[0].price.type).toBe('one_time');
    // @ts-ignore
    expect(result[1].price && result[1].price.type).toBe('one_time');
  });

  it('should return empty array when no one-time items exist', () => {
    const lineItems = [{ price: { type: 'recurring' } }, { price: { type: 'recurring' } }];
    const result = getOneTimeLineItems(lineItems as TLineItemExpanded[]);
    expect(result).toEqual([]);
  });
});

describe('getRecurringLineItems', () => {
  it('should return only recurring line items', () => {
    const lineItems = [
      { price: { type: 'one_time' } },
      { price: { type: 'recurring' } },
      { price: { type: 'recurring' } },
    ];
    const result = getRecurringLineItems(lineItems as TLineItemExpanded[]);
    expect(result).toHaveLength(2);
    // @ts-ignore
    expect(result[0].price && result[0].price.type).toBe('recurring');
    // @ts-ignore
    expect(result[1].price && result[1].price.type).toBe('recurring');
  });

  it('should return empty array when no recurring items exist', () => {
    const lineItems = [{ price: { type: 'one_time' } }, { price: { type: 'one_time' } }];
    const result = getRecurringLineItems(lineItems as TLineItemExpanded[]);
    expect(result).toEqual([]);
  });
});

describe('mergeSubscriptionDataFromLineItems', () => {
  it('should merge subscription data from multiple line items', () => {
    const lineItems = [
      {
        subscription_data: {
          description: 'Item 1',
          metadata: { key1: 'value1' },
        },
      },
      {
        subscription_data: {
          days_until_due: 30,
          metadata: { key2: 'value2' },
        },
      },
    ];
    const result = mergeSubscriptionDataFromLineItems(lineItems as any);
    expect(result).toEqual({
      description: 'Item 1',
      days_until_due: 30,
      metadata: { key1: 'value1', key2: 'value2' },
    });
  });

  it('should return empty object when line items array is empty', () => {
    const result = mergeSubscriptionDataFromLineItems([]);
    expect(result).toEqual({});
  });

  it('should handle line items without subscription_data', () => {
    const lineItems = [
      { price: { type: 'recurring' } },
      {
        subscription_data: {
          description: 'Item 2',
        },
      },
    ];
    const result = mergeSubscriptionDataFromLineItems(lineItems as any);
    expect(result).toEqual({
      description: 'Item 2',
    });
  });

  it('should not include metadata field if no metadata exists', () => {
    const lineItems = [
      {
        subscription_data: {
          description: 'Item 1',
        },
      },
    ];
    const result = mergeSubscriptionDataFromLineItems(lineItems as any);
    expect(result).toEqual({
      description: 'Item 1',
    });
    expect(result.metadata).toBeUndefined();
  });
});

describe('getFastCheckoutAmount', () => {
  it('should return total amount for payment mode', async () => {
    const items = [{ price: { type: 'one_time', currency_id: 'usd', unit_amount: '100' }, quantity: 1 }];
    const result = await getFastCheckoutAmount({
      items: items as TLineItemExpanded[],
      mode: 'payment',
      currencyId: 'usd',
    });
    expect(result).toBe('100');
  });

  it('should return renew amount for setup mode', async () => {
    const items = [
      {
        price: { type: 'recurring', recurring: { usage_type: 'licensed' }, currency_id: 'usd', unit_amount: '100' },
        quantity: 1,
      },
    ];
    const result = await getFastCheckoutAmount({ items: items as any, mode: 'setup', currencyId: 'usd' });
    expect(result).toBe('100');
  });

  it('should multiply renew amount by minimumCycle for setup mode', async () => {
    const items = [
      {
        price: { type: 'recurring', recurring: { usage_type: 'licensed' }, currency_id: 'usd', unit_amount: '100' },
        quantity: 1,
      },
    ];
    const result = await getFastCheckoutAmount({
      items: items as any,
      mode: 'setup',
      currencyId: 'usd',
      trialing: false,
      minimumCycle: 3,
    });
    expect(result).toBe('300');
  });

  it('should return total + renew*(minimumCycle-1) for subscription mode', async () => {
    const items = [
      {
        price: { type: 'recurring', recurring: { usage_type: 'licensed' }, currency_id: 'usd', unit_amount: '100' },
        quantity: 1,
      },
      { price: { type: 'one_time', currency_id: 'usd', unit_amount: '50' }, quantity: 1 },
    ];
    const result = await getFastCheckoutAmount({
      items: items as any,
      mode: 'subscription',
      currencyId: 'usd',
      trialing: false,
      minimumCycle: 3,
    });
    expect(result).toBe('350');
  });

  it('should return total + renew*minimumCycle for subscription mode when trialing', async () => {
    const items = [
      {
        price: { type: 'recurring', recurring: { usage_type: 'licensed' }, currency_id: 'usd', unit_amount: '100' },
        quantity: 1,
      },
      { price: { type: 'one_time', currency_id: 'usd', unit_amount: '50' }, quantity: 1 },
    ];
    const result = await getFastCheckoutAmount({
      items: items as any,
      mode: 'subscription',
      currencyId: 'usd',
      trialing: true,
      minimumCycle: 3,
    });
    expect(result).toBe('350'); // 50 (total, since recurring is 0 during trial) + 100 (renew) * 3
  });

  it('should return 0 for unknown mode', async () => {
    const items = [{ price: { type: 'one_time', currency_id: 'usd', unit_amount: '100' }, quantity: 1 }];
    const result = await getFastCheckoutAmount({ items: items as any, mode: 'unknown', currencyId: 'usd' });
    expect(result).toBe('0');
  });
});

describe('createPaymentBeneficiaries', () => {
  it('should return empty array when beneficiaries array is empty', () => {
    const beneficiaries: PaymentBeneficiary[] = [];
    const result = createPaymentBeneficiaries('100', beneficiaries);
    expect(result).toEqual([]);
  });

  it('should calculate correct amounts based on shares', () => {
    const beneficiaries: PaymentBeneficiary[] = [
      { address: 'addr1', share: '30' },
      { address: 'addr2', share: '70' },
    ];
    const result = createPaymentBeneficiaries('100', beneficiaries);
    expect(result).toHaveLength(2);
    expect(result[0]?.share).toBe('30');
    expect(result[0]?.address).toBe('addr1');
    expect(result[1]?.share).toBe('70');
    expect(result[1]?.address).toBe('addr2');
  });
});

describe('getSubscriptionLineItems', () => {
  it('should filter line items related to subscription', async () => {
    const subscription = { id: 'sub_123' };
    const lineItems = [
      { price_id: 'price_1', price: { type: 'recurring' } },
      { price_id: 'price_2', price: { type: 'recurring' } },
      { price_id: 'price_3', price: { type: 'one_time' } },
    ];

    // Mock SubscriptionItem.findAll
    const mockSubscriptionItems = [
      { subscription_id: 'sub_123', price_id: 'price_1' },
      { subscription_id: 'sub_123', price_id: 'price_2' },
    ];
    jest.spyOn(SubscriptionItem, 'findAll').mockResolvedValue(mockSubscriptionItems as any);

    const result = await getSubscriptionLineItems(subscription as any, lineItems as any);

    expect(result).toHaveLength(2);
    // @ts-ignore
    expect(result[0].price_id).toBe('price_1');
    // @ts-ignore
    expect(result[1].price_id).toBe('price_2');
  });

  it('should include one-time items for primary subscription', async () => {
    const subscription = { id: 'sub_123' };
    const primarySubscription = { id: 'sub_123' };
    const lineItems = [
      { price_id: 'price_1', price: { type: 'recurring' } },
      { price_id: 'price_3', price: { type: 'one_time' } },
    ];

    // Mock SubscriptionItem.findAll
    const mockSubscriptionItems = [{ subscription_id: 'sub_123', price_id: 'price_1' }];
    jest.spyOn(SubscriptionItem, 'findAll').mockResolvedValue(mockSubscriptionItems as any);

    const result = await getSubscriptionLineItems(subscription as any, lineItems as any, primarySubscription as any);

    expect(result).toHaveLength(2);
    // @ts-ignore
    expect(result[0].price_id).toBe('price_1');
    // @ts-ignore
    expect(result[1].price_id).toBe('price_3');
    // @ts-ignore
    expect(result[1].price.type).toBe('one_time');
  });

  it('should handle upsell price ids', async () => {
    const subscription = { id: 'sub_456' };
    const lineItems = [{ price_id: 'price_1', upsell_price_id: 'price_2', price: { type: 'recurring' } }];

    // Mock SubscriptionItem.findAll
    const mockSubscriptionItems = [{ subscription_id: 'sub_456', price_id: 'price_2' }];
    jest.spyOn(SubscriptionItem, 'findAll').mockResolvedValue(mockSubscriptionItems as any);

    const result = await getSubscriptionLineItems(subscription as any, lineItems as any);

    expect(result).toHaveLength(1);
    // @ts-ignore
    expect(result[0].price_id).toBe('price_1');
    // @ts-ignore
    expect(result[0].upsell_price_id).toBe('price_2');
  });

  it('should return empty array when no items match', async () => {
    const subscription = { id: 'sub_789' };
    const lineItems = [{ price_id: 'price_1', price: { type: 'recurring' } }];

    // Mock SubscriptionItem.findAll with empty array
    jest.spyOn(SubscriptionItem, 'findAll').mockResolvedValue([]);

    const result = await getSubscriptionLineItems(subscription as any, lineItems as any);

    expect(result).toHaveLength(0);
  });
});
