// Mock Stripe SDK
const mockStripeCheckoutSessionsRetrieve = jest.fn();
const mockStripeAccountsCreate = jest.fn();
const mockStripeAccountsRetrieve = jest.fn();
const mockStripeAccountLinksCreate = jest.fn();
const mockStripeTransfersCreate = jest.fn();
const mockStripeRefundsCreate = jest.fn();
const mockStripeRefundsRetrieve = jest.fn();
const mockStripePaymentIntentsCreate = jest.fn();
const mockStripeCustomersCreate = jest.fn();
const mockStripeCheckoutSessionsCreate = jest.fn();

jest.mock('stripe', () => {
  return jest.fn(() => ({
    checkout: {
      sessions: {
        retrieve: mockStripeCheckoutSessionsRetrieve,
        create: mockStripeCheckoutSessionsCreate
      }
    },
    accounts: {
      create: mockStripeAccountsCreate,
      retrieve: mockStripeAccountsRetrieve
    },
    accountLinks: {
      create: mockStripeAccountLinksCreate
    },
    transfers: {
      create: mockStripeTransfersCreate
    },
    refunds: {
      create: mockStripeRefundsCreate,
      retrieve: mockStripeRefundsRetrieve
    },
    paymentIntents: {
      create: mockStripePaymentIntentsCreate
    },
    customers: {
      create: mockStripeCustomersCreate
    }
  }));
});

const StripeService = require('../../../services/stripeService');

describe('StripeService', () => {
  let consoleSpy, consoleErrorSpy;

  beforeEach(() => {
    jest.clearAllMocks();

    // Set up console spies
    consoleSpy = jest.spyOn(console, 'log').mockImplementation();
    consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();

    // Set environment variables for tests
    process.env.FRONTEND_URL = 'http://localhost:3000';
  });

  afterEach(() => {
    consoleSpy.mockRestore();
    consoleErrorSpy.mockRestore();
  });

  describe('getCheckoutSession', () => {
    it('should retrieve checkout session successfully', async () => {
      const mockSession = {
        id: 'cs_123456789',
        status: 'complete',
        setup_intent: {
          id: 'seti_123456789',
          payment_method: {
            id: 'pm_123456789',
            type: 'card'
          }
        }
      };

      mockStripeCheckoutSessionsRetrieve.mockResolvedValue(mockSession);

      const result = await StripeService.getCheckoutSession('cs_123456789');

      expect(mockStripeCheckoutSessionsRetrieve).toHaveBeenCalledWith('cs_123456789', {
        expand: ['setup_intent', 'setup_intent.payment_method']
      });
      expect(result).toEqual(mockSession);
    });

    it('should handle checkout session retrieval errors', async () => {
      const stripeError = new Error('Session not found');
      mockStripeCheckoutSessionsRetrieve.mockRejectedValue(stripeError);

      await expect(StripeService.getCheckoutSession('invalid_session'))
        .rejects.toThrow('Session not found');

      expect(consoleErrorSpy).toHaveBeenCalledWith(
        'Error retrieving checkout session:',
        stripeError
      );
    });

    it('should handle missing session ID', async () => {
      const stripeError = new Error('Invalid session ID');
      mockStripeCheckoutSessionsRetrieve.mockRejectedValue(stripeError);

      await expect(StripeService.getCheckoutSession(null))
        .rejects.toThrow('Invalid session ID');
    });
  });

  describe('createConnectedAccount', () => {
    it('should create connected account with default country', async () => {
      const mockAccount = {
        id: 'acct_123456789',
        type: 'express',
        email: 'test@example.com',
        country: 'US',
        capabilities: {
          transfers: { status: 'pending' }
        }
      };

      mockStripeAccountsCreate.mockResolvedValue(mockAccount);

      const result = await StripeService.createConnectedAccount({
        email: 'test@example.com'
      });

      expect(mockStripeAccountsCreate).toHaveBeenCalledWith({
        type: 'express',
        email: 'test@example.com',
        country: 'US',
        capabilities: {
          transfers: { requested: true }
        }
      });
      expect(result).toEqual(mockAccount);
    });

    it('should create connected account with custom country', async () => {
      const mockAccount = {
        id: 'acct_123456789',
        type: 'express',
        email: 'test@example.com',
        country: 'CA',
        capabilities: {
          transfers: { status: 'pending' }
        }
      };

      mockStripeAccountsCreate.mockResolvedValue(mockAccount);

      const result = await StripeService.createConnectedAccount({
        email: 'test@example.com',
        country: 'CA'
      });

      expect(mockStripeAccountsCreate).toHaveBeenCalledWith({
        type: 'express',
        email: 'test@example.com',
        country: 'CA',
        capabilities: {
          transfers: { requested: true }
        }
      });
      expect(result).toEqual(mockAccount);
    });

    it('should handle connected account creation errors', async () => {
      const stripeError = new Error('Invalid email address');
      mockStripeAccountsCreate.mockRejectedValue(stripeError);

      await expect(StripeService.createConnectedAccount({
        email: 'invalid-email'
      })).rejects.toThrow('Invalid email address');

      expect(consoleErrorSpy).toHaveBeenCalledWith(
        'Error creating connected account:',
        stripeError
      );
    });

    it('should handle missing email parameter', async () => {
      const stripeError = new Error('Email is required');
      mockStripeAccountsCreate.mockRejectedValue(stripeError);

      await expect(StripeService.createConnectedAccount({}))
        .rejects.toThrow('Email is required');
    });
  });

  describe('createAccountLink', () => {
    it('should create account link successfully', async () => {
      const mockAccountLink = {
        object: 'account_link',
        url: 'https://connect.stripe.com/setup/e/acct_123456789',
        created: Date.now(),
        expires_at: Date.now() + 3600
      };

      mockStripeAccountLinksCreate.mockResolvedValue(mockAccountLink);

      const result = await StripeService.createAccountLink(
        'acct_123456789',
        'http://localhost:3000/refresh',
        'http://localhost:3000/return'
      );

      expect(mockStripeAccountLinksCreate).toHaveBeenCalledWith({
        account: 'acct_123456789',
        refresh_url: 'http://localhost:3000/refresh',
        return_url: 'http://localhost:3000/return',
        type: 'account_onboarding'
      });
      expect(result).toEqual(mockAccountLink);
    });

    it('should handle account link creation errors', async () => {
      const stripeError = new Error('Account not found');
      mockStripeAccountLinksCreate.mockRejectedValue(stripeError);

      await expect(StripeService.createAccountLink(
        'invalid_account',
        'http://localhost:3000/refresh',
        'http://localhost:3000/return'
      )).rejects.toThrow('Account not found');

      expect(consoleErrorSpy).toHaveBeenCalledWith(
        'Error creating account link:',
        stripeError
      );
    });

    it('should handle invalid URLs', async () => {
      const stripeError = new Error('Invalid URL format');
      mockStripeAccountLinksCreate.mockRejectedValue(stripeError);

      await expect(StripeService.createAccountLink(
        'acct_123456789',
        'invalid-url',
        'invalid-url'
      )).rejects.toThrow('Invalid URL format');
    });
  });

  describe('getAccountStatus', () => {
    it('should retrieve account status successfully', async () => {
      const mockAccount = {
        id: 'acct_123456789',
        details_submitted: true,
        payouts_enabled: true,
        capabilities: {
          transfers: { status: 'active' }
        },
        requirements: {
          pending_verification: [],
          currently_due: [],
          past_due: []
        },
        other_field: 'should_be_filtered_out'
      };

      mockStripeAccountsRetrieve.mockResolvedValue(mockAccount);

      const result = await StripeService.getAccountStatus('acct_123456789');

      expect(mockStripeAccountsRetrieve).toHaveBeenCalledWith('acct_123456789');
      expect(result).toEqual({
        id: 'acct_123456789',
        details_submitted: true,
        payouts_enabled: true,
        capabilities: {
          transfers: { status: 'active' }
        },
        requirements: {
          pending_verification: [],
          currently_due: [],
          past_due: []
        }
      });
    });

    it('should handle account status retrieval errors', async () => {
      const stripeError = new Error('Account not found');
      mockStripeAccountsRetrieve.mockRejectedValue(stripeError);

      await expect(StripeService.getAccountStatus('invalid_account'))
        .rejects.toThrow('Account not found');

      expect(consoleErrorSpy).toHaveBeenCalledWith(
        'Error retrieving account status:',
        stripeError
      );
    });

    it('should handle accounts with incomplete data', async () => {
      const mockAccount = {
        id: 'acct_123456789',
        details_submitted: false,
        payouts_enabled: false,
        capabilities: null,
        requirements: null
      };

      mockStripeAccountsRetrieve.mockResolvedValue(mockAccount);

      const result = await StripeService.getAccountStatus('acct_123456789');

      expect(result).toEqual({
        id: 'acct_123456789',
        details_submitted: false,
        payouts_enabled: false,
        capabilities: null,
        requirements: null
      });
    });
  });

  describe('createTransfer', () => {
    it('should create transfer with default currency and idempotency key', async () => {
      const mockTransfer = {
        id: 'tr_123456789',
        amount: 5000, // $50.00 in cents
        currency: 'usd',
        destination: 'acct_123456789',
        metadata: {
          rentalId: '1',
          ownerId: '2'
        }
      };

      mockStripeTransfersCreate.mockResolvedValue(mockTransfer);

      const result = await StripeService.createTransfer({
        amount: 50.00,
        destination: 'acct_123456789',
        metadata: {
          rentalId: '1',
          ownerId: '2'
        }
      });

      expect(mockStripeTransfersCreate).toHaveBeenCalledWith(
        {
          amount: 5000, // Converted to cents
          currency: 'usd',
          destination: 'acct_123456789',
          metadata: {
            rentalId: '1',
            ownerId: '2'
          }
        },
        { idempotencyKey: 'transfer_rental_1' }
      );
      expect(result).toEqual(mockTransfer);
    });

    it('should create transfer with custom currency and no idempotency key when no rentalId', async () => {
      const mockTransfer = {
        id: 'tr_123456789',
        amount: 5000,
        currency: 'eur',
        destination: 'acct_123456789',
        metadata: {}
      };

      mockStripeTransfersCreate.mockResolvedValue(mockTransfer);

      const result = await StripeService.createTransfer({
        amount: 50.00,
        currency: 'eur',
        destination: 'acct_123456789'
      });

      expect(mockStripeTransfersCreate).toHaveBeenCalledWith(
        {
          amount: 5000,
          currency: 'eur',
          destination: 'acct_123456789',
          metadata: {}
        },
        undefined
      );
      expect(result).toEqual(mockTransfer);
    });

    it('should handle decimal amounts correctly', async () => {
      const mockTransfer = {
        id: 'tr_123456789',
        amount: 12534, // $125.34 in cents
        currency: 'usd',
        destination: 'acct_123456789',
        metadata: {}
      };

      mockStripeTransfersCreate.mockResolvedValue(mockTransfer);

      await StripeService.createTransfer({
        amount: 125.34,
        destination: 'acct_123456789'
      });

      expect(mockStripeTransfersCreate).toHaveBeenCalledWith(
        {
          amount: 12534, // Properly converted to cents
          currency: 'usd',
          destination: 'acct_123456789',
          metadata: {}
        },
        undefined
      );
    });

    it('should handle transfer creation errors', async () => {
      const stripeError = new Error('Insufficient funds');
      mockStripeTransfersCreate.mockRejectedValue(stripeError);

      await expect(StripeService.createTransfer({
        amount: 50.00,
        destination: 'acct_123456789'
      })).rejects.toThrow('Insufficient funds');

      expect(consoleErrorSpy).toHaveBeenCalledWith(
        'Error creating transfer:',
        stripeError
      );
    });

    it('should handle rounding for very small amounts', async () => {
      const mockTransfer = {
        id: 'tr_123456789',
        amount: 1, // $0.005 rounded to 1 cent
        currency: 'usd',
        destination: 'acct_123456789',
        metadata: {}
      };

      mockStripeTransfersCreate.mockResolvedValue(mockTransfer);

      await StripeService.createTransfer({
        amount: 0.005, // Should round to 1 cent
        destination: 'acct_123456789'
      });

      expect(mockStripeTransfersCreate).toHaveBeenCalledWith(
        {
          amount: 1,
          currency: 'usd',
          destination: 'acct_123456789',
          metadata: {}
        },
        undefined
      );
    });
  });

  describe('createRefund', () => {
    it('should create refund with default parameters and idempotency key', async () => {
      const mockRefund = {
        id: 're_123456789',
        amount: 5000, // $50.00 in cents
        payment_intent: 'pi_123456789',
        reason: 'requested_by_customer',
        status: 'succeeded',
        metadata: {
          rentalId: '1'
        }
      };

      mockStripeRefundsCreate.mockResolvedValue(mockRefund);

      const result = await StripeService.createRefund({
        paymentIntentId: 'pi_123456789',
        amount: 50.00,
        metadata: {
          rentalId: '1'
        }
      });

      expect(mockStripeRefundsCreate).toHaveBeenCalledWith(
        {
          payment_intent: 'pi_123456789',
          amount: 5000, // Converted to cents
          metadata: {
            rentalId: '1'
          },
          reason: 'requested_by_customer'
        },
        { idempotencyKey: 'refund_rental_1_5000' }
      );
      expect(result).toEqual(mockRefund);
    });

    it('should create refund with custom reason and no idempotency key when no rentalId', async () => {
      const mockRefund = {
        id: 're_123456789',
        amount: 10000,
        payment_intent: 'pi_123456789',
        reason: 'fraudulent',
        status: 'succeeded',
        metadata: {}
      };

      mockStripeRefundsCreate.mockResolvedValue(mockRefund);

      const result = await StripeService.createRefund({
        paymentIntentId: 'pi_123456789',
        amount: 100.00,
        reason: 'fraudulent'
      });

      expect(mockStripeRefundsCreate).toHaveBeenCalledWith(
        {
          payment_intent: 'pi_123456789',
          amount: 10000,
          metadata: {},
          reason: 'fraudulent'
        },
        undefined
      );
      expect(result).toEqual(mockRefund);
    });

    it('should handle decimal amounts correctly', async () => {
      const mockRefund = {
        id: 're_123456789',
        amount: 12534, // $125.34 in cents
        payment_intent: 'pi_123456789',
        reason: 'requested_by_customer',
        status: 'succeeded',
        metadata: {}
      };

      mockStripeRefundsCreate.mockResolvedValue(mockRefund);

      await StripeService.createRefund({
        paymentIntentId: 'pi_123456789',
        amount: 125.34
      });

      expect(mockStripeRefundsCreate).toHaveBeenCalledWith(
        {
          payment_intent: 'pi_123456789',
          amount: 12534, // Properly converted to cents
          metadata: {},
          reason: 'requested_by_customer'
        },
        undefined
      );
    });

    it('should handle refund creation errors', async () => {
      const stripeError = new Error('Payment intent not found');
      mockStripeRefundsCreate.mockRejectedValue(stripeError);

      await expect(StripeService.createRefund({
        paymentIntentId: 'pi_invalid',
        amount: 50.00
      })).rejects.toThrow('Payment intent not found');

      expect(consoleErrorSpy).toHaveBeenCalledWith(
        'Error creating refund:',
        stripeError
      );
    });

    it('should handle partial refund scenarios', async () => {
      const mockRefund = {
        id: 're_123456789',
        amount: 2500, // Partial refund of $25.00
        payment_intent: 'pi_123456789',
        reason: 'requested_by_customer',
        status: 'succeeded',
        metadata: {
          type: 'partial'
        }
      };

      mockStripeRefundsCreate.mockResolvedValue(mockRefund);

      const result = await StripeService.createRefund({
        paymentIntentId: 'pi_123456789',
        amount: 25.00,
        metadata: {
          type: 'partial'
        }
      });

      expect(result.amount).toBe(2500);
      expect(result.metadata.type).toBe('partial');
    });
  });

  describe('getRefund', () => {
    it('should retrieve refund successfully', async () => {
      const mockRefund = {
        id: 're_123456789',
        amount: 5000,
        payment_intent: 'pi_123456789',
        reason: 'requested_by_customer',
        status: 'succeeded',
        created: Date.now()
      };

      mockStripeRefundsRetrieve.mockResolvedValue(mockRefund);

      const result = await StripeService.getRefund('re_123456789');

      expect(mockStripeRefundsRetrieve).toHaveBeenCalledWith('re_123456789');
      expect(result).toEqual(mockRefund);
    });

    it('should handle refund retrieval errors', async () => {
      const stripeError = new Error('Refund not found');
      mockStripeRefundsRetrieve.mockRejectedValue(stripeError);

      await expect(StripeService.getRefund('re_invalid'))
        .rejects.toThrow('Refund not found');

      expect(consoleErrorSpy).toHaveBeenCalledWith(
        'Error retrieving refund:',
        stripeError
      );
    });

    it('should handle null refund ID', async () => {
      const stripeError = new Error('Invalid refund ID');
      mockStripeRefundsRetrieve.mockRejectedValue(stripeError);

      await expect(StripeService.getRefund(null))
        .rejects.toThrow('Invalid refund ID');
    });
  });

  describe('chargePaymentMethod', () => {
    it('should charge payment method successfully with idempotency key', async () => {
      const mockPaymentIntent = {
        id: 'pi_123456789',
        status: 'succeeded',
        client_secret: 'pi_123456789_secret_test',
        amount: 5000,
        currency: 'usd',
        created: 1234567890,
        latest_charge: {
          payment_method_details: {
            type: 'card',
            card: {
              brand: 'visa',
              last4: '4242'
            }
          }
        }
      };

      mockStripePaymentIntentsCreate.mockResolvedValue(mockPaymentIntent);

      const result = await StripeService.chargePaymentMethod(
        'pm_123456789',
        50.00,
        'cus_123456789',
        { rentalId: '1' }
      );

      expect(mockStripePaymentIntentsCreate).toHaveBeenCalledWith(
        {
          amount: 5000, // Converted to cents
          currency: 'usd',
          payment_method: 'pm_123456789',
          customer: 'cus_123456789',
          confirm: true,
          off_session: true,
          return_url: 'http://localhost:3000/complete-payment',
          metadata: { rentalId: '1' },
          expand: ['latest_charge.payment_method_details']
        },
        { idempotencyKey: 'charge_rental_1' }
      );

      expect(result).toEqual({
        paymentIntentId: 'pi_123456789',
        status: 'succeeded',
        clientSecret: 'pi_123456789_secret_test',
        paymentMethod: {
          type: 'card',
          brand: 'visa',
          last4: '4242'
        },
        chargedAt: new Date(1234567890 * 1000),
        amountCharged: 50.00
      });
    });

    it('should handle payment method charge errors', async () => {
      const stripeError = new Error('Payment method declined');
      mockStripePaymentIntentsCreate.mockRejectedValue(stripeError);

      await expect(StripeService.chargePaymentMethod(
        'pm_invalid',
        50.00,
        'cus_123456789'
      )).rejects.toThrow('Payment method declined');

      expect(consoleErrorSpy).toHaveBeenCalledWith(
        'Error charging payment method:',
        stripeError
      );
    });

    it('should use default frontend URL when not set and no idempotency key without rentalId', async () => {
      delete process.env.FRONTEND_URL;

      const mockPaymentIntent = {
        id: 'pi_123456789',
        status: 'succeeded',
        client_secret: 'pi_123456789_secret_test'
      };

      mockStripePaymentIntentsCreate.mockResolvedValue(mockPaymentIntent);

      await StripeService.chargePaymentMethod(
        'pm_123456789',
        50.00,
        'cus_123456789'
      );

      expect(mockStripePaymentIntentsCreate).toHaveBeenCalledWith(
        expect.objectContaining({
          return_url: 'http://localhost:3000/complete-payment'
        }),
        undefined
      );
    });

    it('should handle decimal amounts correctly', async () => {
      const mockPaymentIntent = {
        id: 'pi_123456789',
        status: 'succeeded',
        client_secret: 'pi_123456789_secret_test'
      };

      mockStripePaymentIntentsCreate.mockResolvedValue(mockPaymentIntent);

      await StripeService.chargePaymentMethod(
        'pm_123456789',
        125.34,
        'cus_123456789'
      );

      expect(mockStripePaymentIntentsCreate).toHaveBeenCalledWith(
        expect.objectContaining({
          amount: 12534 // Properly converted to cents
        }),
        undefined
      );
    });

    it('should handle payment requiring authentication', async () => {
      const mockPaymentIntent = {
        id: 'pi_123456789',
        status: 'requires_action',
        client_secret: 'pi_123456789_secret_test',
        next_action: {
          type: 'use_stripe_sdk'
        }
      };

      mockStripePaymentIntentsCreate.mockResolvedValue(mockPaymentIntent);

      const result = await StripeService.chargePaymentMethod(
        'pm_123456789',
        50.00,
        'cus_123456789'
      );

      expect(result.status).toBe('requires_action');
      expect(result.clientSecret).toBe('pi_123456789_secret_test');
    });
  });

  describe('createCustomer', () => {
    it('should create customer successfully', async () => {
      const mockCustomer = {
        id: 'cus_123456789',
        email: 'test@example.com',
        name: 'John Doe',
        metadata: {
          userId: '123'
        },
        created: Date.now()
      };

      mockStripeCustomersCreate.mockResolvedValue(mockCustomer);

      const result = await StripeService.createCustomer({
        email: 'test@example.com',
        name: 'John Doe',
        metadata: {
          userId: '123'
        }
      });

      expect(mockStripeCustomersCreate).toHaveBeenCalledWith({
        email: 'test@example.com',
        name: 'John Doe',
        metadata: {
          userId: '123'
        }
      });
      expect(result).toEqual(mockCustomer);
    });

    it('should create customer with minimal data', async () => {
      const mockCustomer = {
        id: 'cus_123456789',
        email: 'test@example.com',
        name: null,
        metadata: {}
      };

      mockStripeCustomersCreate.mockResolvedValue(mockCustomer);

      const result = await StripeService.createCustomer({
        email: 'test@example.com'
      });

      expect(mockStripeCustomersCreate).toHaveBeenCalledWith({
        email: 'test@example.com',
        name: undefined,
        metadata: {}
      });
      expect(result).toEqual(mockCustomer);
    });

    it('should handle customer creation errors', async () => {
      const stripeError = new Error('Invalid email format');
      mockStripeCustomersCreate.mockRejectedValue(stripeError);

      await expect(StripeService.createCustomer({
        email: 'invalid-email'
      })).rejects.toThrow('Invalid email format');

      expect(consoleErrorSpy).toHaveBeenCalledWith(
        'Error creating customer:',
        stripeError
      );
    });

    it('should handle duplicate customer errors', async () => {
      const stripeError = new Error('Customer already exists');
      mockStripeCustomersCreate.mockRejectedValue(stripeError);

      await expect(StripeService.createCustomer({
        email: 'existing@example.com',
        name: 'Existing User'
      })).rejects.toThrow('Customer already exists');
    });
  });

  describe('createSetupCheckoutSession', () => {
    it('should create setup checkout session successfully', async () => {
      const mockSession = {
        id: 'cs_123456789',
        url: null,
        client_secret: 'cs_123456789_secret_test',
        customer: 'cus_123456789',
        mode: 'setup',
        ui_mode: 'embedded',
        metadata: {
          type: 'payment_method_setup',
          userId: '123'
        }
      };

      mockStripeCheckoutSessionsCreate.mockResolvedValue(mockSession);

      const result = await StripeService.createSetupCheckoutSession({
        customerId: 'cus_123456789',
        metadata: {
          userId: '123'
        }
      });

      expect(mockStripeCheckoutSessionsCreate).toHaveBeenCalledWith({
        customer: 'cus_123456789',
        payment_method_types: ['card', 'link'],
        mode: 'setup',
        ui_mode: 'embedded',
        redirect_on_completion: 'never',
        metadata: {
          type: 'payment_method_setup',
          userId: '123'
        }
      });
      expect(result).toEqual(mockSession);
    });

    it('should create setup checkout session with minimal data', async () => {
      const mockSession = {
        id: 'cs_123456789',
        url: null,
        client_secret: 'cs_123456789_secret_test',
        customer: 'cus_123456789',
        mode: 'setup',
        ui_mode: 'embedded',
        metadata: {
          type: 'payment_method_setup'
        }
      };

      mockStripeCheckoutSessionsCreate.mockResolvedValue(mockSession);

      const result = await StripeService.createSetupCheckoutSession({
        customerId: 'cus_123456789'
      });

      expect(mockStripeCheckoutSessionsCreate).toHaveBeenCalledWith({
        customer: 'cus_123456789',
        payment_method_types: ['card', 'link'],
        mode: 'setup',
        ui_mode: 'embedded',
        redirect_on_completion: 'never',
        metadata: {
          type: 'payment_method_setup'
        }
      });
      expect(result).toEqual(mockSession);
    });

    it('should handle setup checkout session creation errors', async () => {
      const stripeError = new Error('Customer not found');
      mockStripeCheckoutSessionsCreate.mockRejectedValue(stripeError);

      await expect(StripeService.createSetupCheckoutSession({
        customerId: 'cus_invalid'
      })).rejects.toThrow('Customer not found');

      expect(consoleErrorSpy).toHaveBeenCalledWith(
        'Error creating setup checkout session:',
        stripeError
      );
    });

    it('should handle missing customer ID', async () => {
      const stripeError = new Error('Customer ID is required');
      mockStripeCheckoutSessionsCreate.mockRejectedValue(stripeError);

      await expect(StripeService.createSetupCheckoutSession({}))
        .rejects.toThrow('Customer ID is required');
    });
  });

  describe('Error handling and edge cases', () => {
    it('should handle very large monetary amounts', async () => {
      const mockTransfer = {
        id: 'tr_123456789',
        amount: 99999999, // $999,999.99 in cents
        currency: 'usd',
        destination: 'acct_123456789',
        metadata: {}
      };

      mockStripeTransfersCreate.mockResolvedValue(mockTransfer);

      await StripeService.createTransfer({
        amount: 999999.99,
        destination: 'acct_123456789'
      });

      expect(mockStripeTransfersCreate).toHaveBeenCalledWith(
        {
          amount: 99999999,
          currency: 'usd',
          destination: 'acct_123456789',
          metadata: {}
        },
        undefined
      );
    });

    it('should handle zero amounts', async () => {
      const mockRefund = {
        id: 're_123456789',
        amount: 0,
        payment_intent: 'pi_123456789',
        reason: 'requested_by_customer',
        status: 'succeeded',
        metadata: {}
      };

      mockStripeRefundsCreate.mockResolvedValue(mockRefund);

      await StripeService.createRefund({
        paymentIntentId: 'pi_123456789',
        amount: 0
      });

      expect(mockStripeRefundsCreate).toHaveBeenCalledWith(
        {
          payment_intent: 'pi_123456789',
          amount: 0,
          metadata: {},
          reason: 'requested_by_customer'
        },
        undefined
      );
    });

    it('should handle network timeout errors', async () => {
      const timeoutError = new Error('Request timeout');
      timeoutError.type = 'StripeConnectionError';
      mockStripeTransfersCreate.mockRejectedValue(timeoutError);

      await expect(StripeService.createTransfer({
        amount: 50.00,
        destination: 'acct_123456789'
      })).rejects.toThrow('Request timeout');

      expect(consoleErrorSpy).toHaveBeenCalledWith(
        'Error creating transfer:',
        timeoutError
      );
    });

    it('should handle API key errors', async () => {
      const apiKeyError = new Error('Invalid API key');
      apiKeyError.type = 'StripeAuthenticationError';
      mockStripeCustomersCreate.mockRejectedValue(apiKeyError);

      await expect(StripeService.createCustomer({
        email: 'test@example.com'
      })).rejects.toThrow('Invalid API key');

      expect(consoleErrorSpy).toHaveBeenCalledWith(
        'Error creating customer:',
        apiKeyError
      );
    });
  });

  describe('Idempotency key generation', () => {
    it('should generate different refund idempotency keys for different amounts on same rental', async () => {
      mockStripeRefundsCreate.mockResolvedValue({ id: 're_123' });

      // First refund - $30
      await StripeService.createRefund({
        paymentIntentId: 'pi_123',
        amount: 30.00,
        metadata: { rentalId: 'rental-uuid-123' }
      });

      // Second refund - $20
      await StripeService.createRefund({
        paymentIntentId: 'pi_123',
        amount: 20.00,
        metadata: { rentalId: 'rental-uuid-123' }
      });

      // Verify different idempotency keys were used
      expect(mockStripeRefundsCreate).toHaveBeenNthCalledWith(
        1,
        expect.any(Object),
        { idempotencyKey: 'refund_rental_rental-uuid-123_3000' }
      );
      expect(mockStripeRefundsCreate).toHaveBeenNthCalledWith(
        2,
        expect.any(Object),
        { idempotencyKey: 'refund_rental_rental-uuid-123_2000' }
      );
    });

    it('should generate consistent transfer idempotency key for same rental', async () => {
      mockStripeTransfersCreate.mockResolvedValue({ id: 'tr_123' });

      const rentalId = 'rental-uuid-456';

      // Call twice with same rental
      await StripeService.createTransfer({
        amount: 100.00,
        destination: 'acct_123',
        metadata: { rentalId }
      });

      await StripeService.createTransfer({
        amount: 100.00,
        destination: 'acct_123',
        metadata: { rentalId }
      });

      // Both should have the same idempotency key
      expect(mockStripeTransfersCreate).toHaveBeenNthCalledWith(
        1,
        expect.any(Object),
        { idempotencyKey: 'transfer_rental_rental-uuid-456' }
      );
      expect(mockStripeTransfersCreate).toHaveBeenNthCalledWith(
        2,
        expect.any(Object),
        { idempotencyKey: 'transfer_rental_rental-uuid-456' }
      );
    });

    it('should generate consistent charge idempotency key for same rental', async () => {
      mockStripePaymentIntentsCreate.mockResolvedValue({
        id: 'pi_123',
        status: 'succeeded',
        client_secret: 'secret',
        created: Date.now() / 1000,
        latest_charge: null
      });

      const rentalId = 'rental-uuid-789';

      await StripeService.chargePaymentMethod(
        'pm_123',
        100.00,
        'cus_123',
        { rentalId }
      );

      expect(mockStripePaymentIntentsCreate).toHaveBeenCalledWith(
        expect.any(Object),
        { idempotencyKey: 'charge_rental_rental-uuid-789' }
      );
    });
  });
});