import dayjs from '../../src/libs/dayjs';
import {
  getDaysUntilCancel,
  getDaysUntilDue,
  getDueUnit,
  getMaxRetryCount,
  getMinRetryMail,
  getSubscriptionStakeSetup,
  getSubscriptionTrialSetup,
  shouldCancelSubscription,
  getSubscriptionStakeAmountSetup,
  checkUsageReportEmpty,
  calculateRecommendedRechargeAmount,
  getSubscriptionCycleSetup,
} from '../../src/libs/subscription';
import { PaymentMethod, Subscription, SubscriptionItem, UsageRecord, Price } from '../../src/store/models';

describe('getDueUnit', () => {
  it('should return 60 for recurring interval of "hour"', () => {
    const result = getDueUnit('hour');
    expect(result).toBe(60);
  });

  it('should return 3600 for recurring interval of "day"', () => {
    const result = getDueUnit('day');
    expect(result).toBe(3600);
  });

  it('should return 86400 for recurring interval other than "hour" or "day"', () => {
    expect(getDueUnit('week')).toBe(86400);
    expect(getDueUnit('month')).toBe(86400);
    expect(getDueUnit('year')).toBe(86400);
  });
});

describe('getDaysUntilDue', () => {
  it('should return the number of days from the query when days_until_due is present', () => {
    const query = { days_until_due: '5' };
    const result = getDaysUntilDue(query);
    expect(result).toBe(5);
  });

  it('should return the number of days from the environment variable when days_until_due is not present in the query', () => {
    process.env.PAYMENT_DAYS_UNTIL_DUE = '10';
    const result = getDaysUntilDue({});
    expect(result).toBe(10);
    delete process.env.PAYMENT_DAYS_UNTIL_DUE;
  });

  it('should return 6 when days_until_due is not present in the query and the environment variable', () => {
    const result = getDaysUntilDue({});
    expect(result).toBe(6);
  });

  it('should return 6 when days_until_due is not a number', () => {
    const query = { days_until_due: 'not a number' };
    const result = getDaysUntilDue(query);
    expect(result).toBe(6);
  });

  it('should return 6 when days_until_due is negative', () => {
    const query = { days_until_due: '-1' };
    const result = getDaysUntilDue(query);
    expect(result).toBe(6);
  });

  it('should return 0 when days_until_due is negative', () => {
    expect(getDaysUntilDue({ days_until_due: '0' })).toEqual(0);
    expect(getDaysUntilDue({ days_until_due: 0 })).toEqual(0);
  });
});

describe('getMaxRetryCount', () => {
  it('should return 10 when interval is hour', () => {
    const result = getMaxRetryCount('hour');
    expect(result).toBe(10);
  });

  it('should return 15 when interval is day', () => {
    const result = getMaxRetryCount('day');
    expect(result).toBe(15);
  });

  it('should return 18 when interval is week', () => {
    const result = getMaxRetryCount('week');
    expect(result).toBe(18);
  });

  it('should return 20 when interval is any other value', () => {
    const result = getMaxRetryCount('month');
    expect(result).toBe(20);
  });
});

describe('getMinRetryMail', () => {
  it('should return 9 when interval is hour', () => {
    const result = getMinRetryMail('hour');
    expect(result).toBe(9);
  });

  it('should return 13 when interval is day', () => {
    const result = getMinRetryMail('day');
    expect(result).toBe(13);
  });

  it('should return 15 when interval is week', () => {
    const result = getMinRetryMail('week');
    expect(result).toBe(15);
  });

  it('should return 15 when interval is any other value', () => {
    const result = getMinRetryMail('month');
    expect(result).toBe(15);
  });
});

describe('getDaysUntilCancel', () => {
  it('should return the number of days from the query when days_until_cancel is present', () => {
    const query = { days_until_cancel: '5' };
    const result = getDaysUntilCancel(query);
    expect(result).toBe(5);
  });

  it('should return the number of days from the environment variable when days_until_cancel is not present in the query', () => {
    process.env.PAYMENT_DAYS_UNTIL_CANCEL = '10';
    const result = getDaysUntilCancel({});
    expect(result).toBe(10);
    delete process.env.PAYMENT_DAYS_UNTIL_CANCEL;
  });

  it('should return 0 when days_until_cancel is not present in the query and the environment variable', () => {
    const result = getDaysUntilCancel({});
    expect(result).toBe(0);
  });

  it('should return 0 when days_until_cancel is not a number', () => {
    const query = { days_until_cancel: 'not a number' };
    const result = getDaysUntilCancel(query);
    expect(result).toBe(0);
  });

  it('should return 0 when days_until_cancel is negative', () => {
    const query = { days_until_cancel: '-1' };
    const result = getDaysUntilCancel(query);
    expect(result).toBe(0);
  });

  it('should return 0 when days_until_cancel is zero', () => {
    expect(getDaysUntilCancel({ days_until_due: '0' })).toEqual(0);
    expect(getDaysUntilCancel({ days_until_due: 0 })).toEqual(0);
  });
});

describe('shouldCancelSubscription', () => {
  let now: number;

  beforeEach(() => {
    now = dayjs().unix();
  });

  it('should return true when status is "past_due" and cancel_at is less than or equal to now', () => {
    const subscription = { status: 'past_due', cancel_at: now - 1 };
    const result = shouldCancelSubscription(subscription as any);
    expect(result).toBe(true);
  });

  it('should return true when status is "active" and cancel_at is less than or equal to now', () => {
    const subscription = { status: 'active', cancel_at: now - 1 };
    const result = shouldCancelSubscription(subscription as any);
    expect(result).toBe(true);
  });

  it('should return false when status is "active" and current_period_end is less than or equal to now', () => {
    const subscription = { status: 'active', current_period_end: now - 1 };
    const result = shouldCancelSubscription(subscription as any);
    expect(result).toBe(false);
  });

  it('should return true when status is "active" and current_period_end is less than or equal to now', () => {
    const subscription = { status: 'active', current_period_end: now - 1, cancel_at_period_end: true };
    const result = shouldCancelSubscription(subscription as any);
    expect(result).toBe(true);
  });

  it('should return false when status is not "past_due" or "active"', () => {
    const subscription = { status: 'inactive', cancel_at: now - 1 };
    const result = shouldCancelSubscription(subscription as any);
    expect(result).toBe(false);
  });

  it('should return false when cancel_at is greater than now', () => {
    const subscription = { status: 'active', cancel_at: now + 60 };
    const result = shouldCancelSubscription(subscription as any);
    expect(result).toBe(false);
  });

  it('should return false when current_period_end is greater than now #1', () => {
    const subscription = { status: 'active', current_period_end: now + 60 };
    const result = shouldCancelSubscription(subscription as any);
    expect(result).toBe(false);
  });

  it('should return false when current_period_end is greater than now #2', () => {
    const subscription = { status: 'active', current_period_end: now + 60, cancel_at_period_end: false };
    const result = shouldCancelSubscription(subscription as any);
    expect(result).toBe(false);
  });
});

describe('getSubscriptionStakeSetup', () => {
  const items: any[] = [
    {
      price: {
        type: 'one_time',
        currency_options: [
          {
            currency_id: 'usd',
            unit_amount: '1',
          },
        ],
      },
      quantity: 1,
    },
    {
      price: {
        type: 'recurring',
        currency_options: [
          {
            currency_id: 'usd',
            unit_amount: '1',
          },
        ],
        recurring: {
          interval: 'day',
          interval_count: '1',
          usage_type: 'licensed',
        },
      },
      quantity: 2,
    },
    {
      price: {
        type: 'recurring',
        currency_options: [
          {
            currency_id: 'usd',
            unit_amount: '1',
          },
        ],
        recurring: {
          interval: 'day',
          interval_count: '1',
          usage_type: 'metered',
        },
      },
      quantity: 2,
    },
  ];

  it('should calculate staking for recurring licensed price type #1', () => {
    const result = getSubscriptionStakeSetup(items, 'usd');
    expect(result.licensed.toString()).toBe('2');
    expect(result.metered.toString()).toBe('2');
  });

  it('should calculate staking for recurring licensed price type #2', () => {
    const result = getSubscriptionStakeSetup(items.slice(0, 2), 'usd');
    expect(result.licensed.toString()).toBe('2');
    expect(result.metered.toString()).toBe('0');
  });

  it('should calculate staking for recurring metered price type when billingThreshold is 0 #1', () => {
    const result = getSubscriptionStakeSetup(items.slice(2, 3), 'usd', '10');
    expect(result.licensed.toString()).toBe('10');
    expect(result.metered.toString()).toBe('0');
  });

  it('should calculate staking for recurring metered price type when billingThreshold is 0 #2', () => {
    const result = getSubscriptionStakeSetup(items, 'usd', '0');
    expect(result.licensed.toString()).toBe('2');
    expect(result.metered.toString()).toBe('2');
  });

  it('should calculate staking for recurring metered price type when billingThreshold is greater than 0', () => {
    const result = getSubscriptionStakeSetup(items, 'usd', '10');
    expect(result.licensed.toString()).toBe('10');
    expect(result.metered.toString()).toBe('0');
  });
});

describe('getSubscriptionTrialSetup', () => {
  it('should return trialInDays and trialEnd when data is provided', () => {
    const data: any = { trial_period_days: '10', trial_end: '20', trial_currency: 'USD,EUR' };
    const currencyId = 'USD';
    const result = getSubscriptionTrialSetup(data, currencyId);
    expect(result).toEqual({ trialInDays: 10, trialEnd: 20 });
  });

  it('should set trialInDays and trialEnd to 0 if currencyId is not in the trialCurrencyIds list', () => {
    const data: any = { trial_period_days: '10', trial_end: '20', trial_currency: 'USD,EUR' };
    const currencyId = 'JPY'; // currency code not included in the trialCurrencyIds list
    const result = getSubscriptionTrialSetup(data, currencyId);
    expect(result).toEqual({ trialInDays: 0, trialEnd: 0 });
  });

  it('should set trialInDays and trialEnd to 0 when values are not provided', () => {
    const data: any = {};
    const currencyId = 'USD';
    const result = getSubscriptionTrialSetup(data, currencyId);
    expect(result).toEqual({ trialInDays: 0, trialEnd: 0 });
  });
});

describe('getSubscriptionStakeAmountSetup', () => {
  let mockSubscription: Subscription;
  let mockPaymentMethod: PaymentMethod;
  let mockGetOcapClient: jest.Mock;
  let mockGetTx: jest.Mock;

  beforeEach(() => {
    mockSubscription = {
      payment_details: {
        arcblock: {
          staking: {
            tx_hash: 'mock_tx_hash',
          },
        },
      },
    } as Subscription;

    mockGetTx = jest.fn();
    mockGetOcapClient = jest.fn().mockReturnValue({
      getTx: mockGetTx,
    });

    // @ts-ignore
    mockPaymentMethod = {
      type: 'arcblock',
      getOcapClient: mockGetOcapClient,
    };
  });

  it('should return null if payment method is not arcblock', async () => {
    mockPaymentMethod.type = 'other';
    const result = await getSubscriptionStakeAmountSetup(mockSubscription, mockPaymentMethod);
    expect(result).toBeNull();
  });

  it('should return null if tx_hash is missing', async () => {
    // @ts-ignore
    mockSubscription.payment_details.arcblock.staking.tx_hash = undefined;
    const result = await getSubscriptionStakeAmountSetup(mockSubscription, mockPaymentMethod);
    expect(result).toBeNull();
  });

  it('should return null if getTx info is null', async () => {
    mockGetTx.mockResolvedValue({ info: null });
    const result = await getSubscriptionStakeAmountSetup(mockSubscription, mockPaymentMethod);
    expect(result).toBeNull();
  });

  it('should return null if inputs are empty', async () => {
    mockGetTx.mockResolvedValue({
      info: {
        tx: {
          itxJson: {
            inputs: [],
          },
        },
      },
    });
    const result = await getSubscriptionStakeAmountSetup(mockSubscription, mockPaymentMethod);
    expect(result).toBeNull();
  });

  it('should calculate stake amount correctly', async () => {
    mockGetTx.mockResolvedValue({
      info: {
        tx: {
          itxJson: {
            inputs: [
              {
                tokens: [
                  { address: 'addr1', value: '100' },
                  { address: 'addr2', value: '200' },
                ],
              },
              {
                tokens: [
                  { address: 'addr1', value: '300' },
                  { address: 'addr3', value: '400' },
                ],
              },
            ],
          },
        },
      },
    });

    const result = await getSubscriptionStakeAmountSetup(mockSubscription, mockPaymentMethod);
    expect(result).toEqual({
      addr1: '400',
      addr2: '200',
      addr3: '400',
    });
  });
});

// 模拟的依赖项
const mockSubscriptionItems = [
  { id: 'item_1', price_id: 'price_1', quantity: 1 },
  { id: 'item_2', price_id: 'price_2', quantity: 1 },
];

const mockExpandedItems = [
  { id: 'item_1', price: { recurring: { usage_type: 'metered' } } },
  { id: 'item_2', price: { recurring: { usage_type: 'metered' } } },
];

const mockUsageRecordsEmpty: any[] = [];
const mockUsageRecordsWithData = [{ id: 'usage_1' }];

describe('checkUsageReportEmpty', () => {
  const subscription = { id: 'sub_123' };
  const usageReportStart = 1622505600;
  const usageReportEnd = 1622592000;

  beforeEach(() => {
    // Reset any state if necessary
  });

  it('should return true if there are no usage records', async () => {
    // Mock the behavior of the functions directly
    jest.spyOn(SubscriptionItem, 'findAll').mockResolvedValue(mockSubscriptionItems as any);
    jest.spyOn(Price, 'expand').mockResolvedValue(mockExpandedItems as any);
    jest.spyOn(UsageRecord, 'findAll').mockResolvedValue(mockUsageRecordsEmpty);

    const result = await checkUsageReportEmpty(subscription as any, usageReportStart, usageReportEnd);
    expect(result).toBe(true);
  });

  it('should return false if there are usage records', async () => {
    jest.spyOn(SubscriptionItem, 'findAll').mockResolvedValue(mockSubscriptionItems as any);
    jest.spyOn(Price, 'expand').mockResolvedValue(mockExpandedItems as any);
    jest.spyOn(UsageRecord, 'findAll').mockResolvedValue(mockUsageRecordsWithData as any);

    const result = await checkUsageReportEmpty(subscription as any, usageReportStart, usageReportEnd);
    expect(result).toBe(false);
  });

  it('should handle multiple metered items', async () => {
    jest.spyOn(SubscriptionItem, 'findAll').mockResolvedValue(mockSubscriptionItems as any);
    jest.spyOn(Price, 'expand').mockResolvedValue(mockExpandedItems as any);

    jest
      .spyOn(UsageRecord, 'findAll')
      .mockResolvedValueOnce(mockUsageRecordsEmpty)
      .mockResolvedValueOnce(mockUsageRecordsWithData as any);

    const result = await checkUsageReportEmpty(subscription as any, usageReportStart, usageReportEnd);
    expect(result).toBe(false);
  });
});

describe('calculateRecommendedRechargeAmount', () => {
  // 测试空订阅列表
  it('should return zero amount for empty subscriptions', () => {
    const result = calculateRecommendedRechargeAmount([], 'usd');
    expect(result).toEqual({ amount: '0', cycle: 0, desc: 'no subscription', interval: '' });
  });

  // 测试只有按小时订阅
  it('should recommend weekly cycle for hourly subscriptions', () => {
    const subscriptions = [
      {
        items: [
          {
            quantity: 2,
            price: {
              type: 'recurring',
              recurring: { interval: 'hour', interval_count: 1 },
              unit_amount: '10',
              currency_id: 'usd',
            },
          },
        ],
      },
    ];

    const result = calculateRecommendedRechargeAmount(subscriptions, 'usd');
    expect(result.cycle).toBe(7 * 24 * 60 * 60); // 一周的秒数
    expect(result.desc).toBe('weekly');
    expect(result.interval).toBe('week');
    // 每小时10，一周168小时，两个单位，总计 10 * 168 * 2 = 3360
    expect(result.amount).toBe('3360');
  });

  // 测试只有按天订阅
  it('should recommend weekly cycle for daily subscriptions', () => {
    const subscriptions = [
      {
        items: [
          {
            quantity: 1,
            price: {
              type: 'recurring',
              recurring: { interval: 'day', interval_count: 1 },
              unit_amount: '100',
              currency_id: 'usd',
            },
          },
        ],
      },
    ];

    const result = calculateRecommendedRechargeAmount(subscriptions, 'usd');
    expect(result.cycle).toBe(7 * 24 * 60 * 60);
    expect(result.desc).toBe('weekly');
    expect(result.interval).toBe('week');
    // 每天100，一周7天，总计 100 * 7 = 700
    expect(result.amount).toBe('700');
  });

  // 测试只有按月订阅
  it('should recommend monthly cycle for monthly subscriptions', () => {
    const subscriptions = [
      {
        items: [
          {
            quantity: 1,
            price: {
              type: 'recurring',
              recurring: { interval: 'month', interval_count: 1, usage_type: 'licensed' },
              unit_amount: '500',
              currency_id: 'usd',
            },
          },
        ],
      },
    ];

    const result = calculateRecommendedRechargeAmount(subscriptions, 'usd');
    expect(result.cycle).toBe(30 * 24 * 60 * 60);
    expect(result.desc).toBe('monthly');
    expect(result.interval).toBe('month');
    expect(result.amount).toBe('500');
  });

  // 测试中间档金额计算 (4x)
  it('should calculate the medium preset amount correctly (4x)', () => {
    const subscriptions = [
      {
        items: [
          {
            quantity: 1,
            price: {
              type: 'recurring',
              recurring: { interval: 'month', interval_count: 1, usage_type: 'licensed' },
              unit_amount: '500',
              currency_id: 'usd',
            },
          },
        ],
      },
    ];

    const result = calculateRecommendedRechargeAmount(subscriptions, 'usd');
    expect(result.amount).toBe('500');

    // 在UI中，我们应用Math.ceil(500 * 4) = 2000作为中间档预设值
    const baseAmount = result.amount;
    const mediumPresetAmount = Math.ceil(Number(baseAmount) * 4).toString();
    expect(mediumPresetAmount).toBe('2000');
  });

  // 测试高档金额计算 (8x)
  it('should calculate the high preset amount correctly (8x)', () => {
    const subscriptions = [
      {
        items: [
          {
            quantity: 1,
            price: {
              type: 'recurring',
              recurring: { interval: 'month', interval_count: 1, usage_type: 'licensed' },
              unit_amount: '500',
              currency_id: 'usd',
            },
          },
        ],
      },
    ];

    const result = calculateRecommendedRechargeAmount(subscriptions, 'usd');

    // 在UI中，我们应用Math.ceil(500 * 8) = 4000作为高档预设值
    const baseAmount = result.amount;
    const highPresetAmount = Math.ceil(Number(baseAmount) * 8).toString();
    expect(highPresetAmount).toBe('4000');
  });

  // 测试前端默认选择中间档
  it('should select medium preset by default in the UI', () => {
    const subscriptions = [
      {
        items: [
          {
            quantity: 1,
            price: {
              type: 'recurring',
              recurring: { interval: 'month', interval_count: 1, usage_type: 'licensed' },
              unit_amount: '500',
              currency_id: 'usd',
            },
          },
        ],
      },
    ];

    const result = calculateRecommendedRechargeAmount(subscriptions, 'usd');
    const baseAmount = result.amount;

    // 验证前端默认选择的中间档金额是基础金额的4倍
    const defaultSelectedAmount = Math.ceil(Number(baseAmount) * 4).toString();
    expect(defaultSelectedAmount).toBe('2000');
  });

  // 测试只有按年订阅
  it('should recommend yearly cycle for yearly subscriptions', () => {
    const subscriptions = [
      {
        items: [
          {
            quantity: 1,
            price: {
              type: 'recurring',
              recurring: { interval: 'year', interval_count: 1 },
              unit_amount: '1200',
              currency_id: 'usd',
            },
          },
        ],
      },
    ];

    const result = calculateRecommendedRechargeAmount(subscriptions, 'usd');
    expect(result.cycle).toBe(365 * 24 * 60 * 60);
    expect(result.desc).toBe('yearly');
    expect(result.amount).toBe('1200');
  });

  // 测试混合按小时和按年的订阅
  it('should recommend monthly cycle for mixed hourly and yearly subscriptions', () => {
    const subscriptions = [
      {
        items: [
          {
            quantity: 1,
            price: {
              type: 'recurring',
              recurring: { interval: 'hour', interval_count: 1 },
              unit_amount: '5',
              currency_id: 'usd',
            },
          },
        ],
      },
      {
        items: [
          {
            quantity: 1,
            price: {
              type: 'recurring',
              recurring: { interval: 'year', interval_count: 1 },
              unit_amount: '1200',
              currency_id: 'usd',
            },
          },
        ],
      },
    ];

    const result = calculateRecommendedRechargeAmount(subscriptions, 'usd');
    expect(result.cycle).toBe(30 * 24 * 60 * 60);
    expect(result.desc).toBe('monthly');
    // 每小时5，一个月约720小时 + 年费1200/12个月
    // 5 * 720 + 1200/12 = 3600 + 100 = 3700
    expect(result.amount).toBe('3700');
  });

  // 测试混合小时、月、年的复杂订阅
  it('should handle complex subscription combinations', () => {
    const subscriptions = [
      {
        items: [
          {
            quantity: 2,
            price: {
              type: 'recurring',
              recurring: { interval: 'hour', interval_count: 1 },
              unit_amount: '5',
              currency_id: 'usd',
            },
          },
          {
            quantity: 1,
            price: {
              type: 'recurring',
              recurring: { interval: 'month', interval_count: 1, usage_type: 'licensed' },
              unit_amount: '200',
              currency_id: 'usd',
            },
          },
        ],
      },
      {
        items: [
          {
            quantity: 1,
            price: {
              type: 'recurring',
              recurring: { interval: 'year', interval_count: 1 },
              unit_amount: '1200',
              currency_id: 'usd',
            },
          },
          {
            quantity: 1,
            price: {
              type: 'one_time',
              unit_amount: '50',
              currency_id: 'usd',
            },
          },
        ],
      },
    ];

    const result = calculateRecommendedRechargeAmount(subscriptions, 'usd');
    expect(result.cycle).toBe(30 * 24 * 60 * 60);
    expect(result.desc).toBe('monthly');
    // 每小时5×2数量=10，一个月约720小时 + 每月200 + 年费1200/12个月 + 一次性费用50
    // 10 * 720 + 200 + 1200/12 + 50 = 7200 + 200 + 100 + 50 = 7550
    expect(result.amount).toBe('7550');
  });

  // 测试不同货币的处理
  it('should handle currency options', () => {
    const subscriptions = [
      {
        items: [
          {
            quantity: 1,
            price: {
              type: 'recurring',
              recurring: { interval: 'month', interval_count: 1, usage_type: 'licensed' },
              currency_id: 'eth',
              currency_options: [
                { currency_id: 'usd', unit_amount: '300' },
                { currency_id: 'btc', unit_amount: '0.01' },
              ],
            },
          },
        ],
      },
    ];

    const result = calculateRecommendedRechargeAmount(subscriptions, 'usd');
    expect(result.cycle).toBe(30 * 24 * 60 * 60);
    expect(result.desc).toBe('monthly');
    expect(result.amount).toBe('300');
  });
});

describe('getSubscriptionCycleSetup', () => {
  const mockRecurring = {
    interval: 'day',
    interval_count: 1,
    usage_type: 'licensed',
  } as any;

  it('should return normal setup without catchUp option', () => {
    const previousPeriodEnd = dayjs().subtract(1, 'day').unix();
    const result = getSubscriptionCycleSetup(mockRecurring, previousPeriodEnd);

    expect(result.missedPeriods).toBe(0);
    expect(result.period.start).toBe(previousPeriodEnd);
    expect(result.recovery).toBeUndefined();
  });

  it('should detect and calculate missed periods with catchUp enabled', () => {
    const previousPeriodEnd = dayjs().subtract(5, 'days').unix(); // 5 days ago
    const result = getSubscriptionCycleSetup(mockRecurring, previousPeriodEnd, {
      catchUp: true,
      maxMissedPeriods: 10,
    });

    expect(result.missedPeriods).toBeGreaterThan(0);
    expect(result.recovery).toBeDefined();
    expect(result.recovery?.originalPeriodEnd).toBe(previousPeriodEnd);
    expect(result.recovery?.periodsSkipped).toBeGreaterThan(0);
    expect(result.period.start).toBeGreaterThan(previousPeriodEnd);
  });

  it('should limit missed periods to maxMissedPeriods', () => {
    const previousPeriodEnd = dayjs().subtract(100, 'days').unix(); // Very old
    const result = getSubscriptionCycleSetup(mockRecurring, previousPeriodEnd, {
      catchUp: true,
      maxMissedPeriods: 10,
    });

    expect(result.missedPeriods).toBe(10);
    expect(result.recovery?.wasLimited).toBe(true);
  });

  it('should handle future period end without catchUp', () => {
    const futurePeriodEnd = dayjs().add(1, 'day').unix();
    const result = getSubscriptionCycleSetup(mockRecurring, futurePeriodEnd, {
      catchUp: true,
    });

    expect(result.missedPeriods).toBe(0);
    expect(result.period.start).toBe(futurePeriodEnd);
  });
});
