import { describe, it, expect, vi, beforeEach } from 'vitest';

// Mock apiClient
vi.mock('../client', () => ({
  default: {
    get: vi.fn(),
    post: vi.fn(),
    delete: vi.fn(),
  },
}));

import {
  getPaymentConfig,
  getApiKeys,
  saveApiKeys,
  validateApiKeys,
  revalidateApiKeys,
  deleteApiKeys,
  getConnectStatus,
  initiateConnectOnboarding,
  refreshConnectOnboardingLink,
  createAccountSession,
  refreshConnectStatus,
  getTransactions,
  getTransaction,
  getTransactionSummary,
  getStripeCharges,
  getStripePayouts,
  getStripeBalance,
  exportTransactions,
  getTransactionDetail,
  refundTransaction,
  getSubscriptionPlans,
  createCheckoutSession,
  getSubscriptions,
  cancelSubscription,
  reactivateSubscription,
} from '../payments';
import apiClient from '../client';

describe('payments API', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  // ============================================================================
  // Configuration
  // ============================================================================

  describe('getPaymentConfig', () => {
    it('fetches unified payment configuration', async () => {
      const mockConfig = {
        payment_mode: 'connect' as const,
        tier: 'professional',
        tier_allows_payments: true,
        stripe_configured: true,
        can_accept_payments: true,
        api_keys: null,
        connect_account: {
          id: 1,
          business: 1,
          business_name: 'Test Business',
          business_subdomain: 'test',
          stripe_account_id: 'acct_123',
          account_type: 'express' as const,
          status: 'active' as const,
          charges_enabled: true,
          payouts_enabled: true,
          details_submitted: true,
          onboarding_complete: true,
          onboarding_link: null,
          onboarding_link_expires_at: null,
          is_onboarding_link_valid: false,
          created_at: '2024-01-01T00:00:00Z',
          updated_at: '2024-01-01T00:00:00Z',
        },
      };
      vi.mocked(apiClient.get).mockResolvedValue({ data: mockConfig });

      const result = await getPaymentConfig();

      expect(apiClient.get).toHaveBeenCalledWith('/payments/config/status/');
      expect(result.data).toEqual(mockConfig);
    });
  });

  // ============================================================================
  // API Keys
  // ============================================================================

  describe('getApiKeys', () => {
    it('fetches current API key configuration', async () => {
      const mockResponse = {
        configured: true,
        id: 1,
        status: 'active' as const,
        secret_key_masked: 'sk_live_****1234',
        publishable_key_masked: 'pk_live_****5678',
        last_validated_at: '2024-01-01T00:00:00Z',
        stripe_account_id: 'acct_123',
        stripe_account_name: 'Test Account',
        validation_error: '',
      };
      vi.mocked(apiClient.get).mockResolvedValue({ data: mockResponse });

      const result = await getApiKeys();

      expect(apiClient.get).toHaveBeenCalledWith('/payments/api-keys/');
      expect(result.data).toEqual(mockResponse);
    });

    it('returns unconfigured response when no keys exist', async () => {
      const mockResponse = {
        configured: false,
        message: 'No API keys configured',
      };
      vi.mocked(apiClient.get).mockResolvedValue({ data: mockResponse });

      const result = await getApiKeys();

      expect(result.data.configured).toBe(false);
      expect(result.data.message).toBe('No API keys configured');
    });
  });

  describe('saveApiKeys', () => {
    it('saves API keys with correct payload', async () => {
      const mockApiKeysInfo = {
        id: 1,
        status: 'active' as const,
        secret_key_masked: 'sk_live_****1234',
        publishable_key_masked: 'pk_live_****5678',
        last_validated_at: '2024-01-01T00:00:00Z',
        stripe_account_id: 'acct_123',
        stripe_account_name: 'Test Account',
        validation_error: '',
        created_at: '2024-01-01T00:00:00Z',
        updated_at: '2024-01-01T00:00:00Z',
      };
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockApiKeysInfo });

      const result = await saveApiKeys('sk_live_123456', 'pk_live_789012');

      expect(apiClient.post).toHaveBeenCalledWith('/payments/api-keys/', {
        secret_key: 'sk_live_123456',
        publishable_key: 'pk_live_789012',
      });
      expect(result.data).toEqual(mockApiKeysInfo);
    });
  });

  describe('validateApiKeys', () => {
    it('validates API keys without saving', async () => {
      const mockValidation = {
        valid: true,
        account_id: 'acct_123',
        account_name: 'Test Account',
        environment: 'live',
      };
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockValidation });

      const result = await validateApiKeys('sk_live_123456', 'pk_live_789012');

      expect(apiClient.post).toHaveBeenCalledWith('/payments/api-keys/validate/', {
        secret_key: 'sk_live_123456',
        publishable_key: 'pk_live_789012',
      });
      expect(result.data.valid).toBe(true);
      expect(result.data.account_id).toBe('acct_123');
    });

    it('returns validation error for invalid keys', async () => {
      const mockValidation = {
        valid: false,
        error: 'Invalid API key provided',
      };
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockValidation });

      const result = await validateApiKeys('sk_invalid', 'pk_invalid');

      expect(result.data.valid).toBe(false);
      expect(result.data.error).toBe('Invalid API key provided');
    });
  });

  describe('revalidateApiKeys', () => {
    it('revalidates stored API keys', async () => {
      const mockValidation = {
        valid: true,
        account_id: 'acct_123',
        account_name: 'Test Account',
        environment: 'live',
      };
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockValidation });

      const result = await revalidateApiKeys();

      expect(apiClient.post).toHaveBeenCalledWith('/payments/api-keys/revalidate/');
      expect(result.data.valid).toBe(true);
    });
  });

  describe('deleteApiKeys', () => {
    it('deletes stored API keys', async () => {
      const mockResponse = {
        success: true,
        message: 'API keys deleted successfully',
      };
      vi.mocked(apiClient.delete).mockResolvedValue({ data: mockResponse });

      const result = await deleteApiKeys();

      expect(apiClient.delete).toHaveBeenCalledWith('/payments/api-keys/delete/');
      expect(result.data.success).toBe(true);
    });
  });

  // ============================================================================
  // Stripe Connect
  // ============================================================================

  describe('getConnectStatus', () => {
    it('fetches Connect account status', async () => {
      const mockConnectAccount = {
        id: 1,
        business: 1,
        business_name: 'Test Business',
        business_subdomain: 'test',
        stripe_account_id: 'acct_123',
        account_type: 'express' as const,
        status: 'active' as const,
        charges_enabled: true,
        payouts_enabled: true,
        details_submitted: true,
        onboarding_complete: true,
        onboarding_link: null,
        onboarding_link_expires_at: null,
        is_onboarding_link_valid: false,
        created_at: '2024-01-01T00:00:00Z',
        updated_at: '2024-01-01T00:00:00Z',
      };
      vi.mocked(apiClient.get).mockResolvedValue({ data: mockConnectAccount });

      const result = await getConnectStatus();

      expect(apiClient.get).toHaveBeenCalledWith('/payments/connect/status/');
      expect(result.data.stripe_account_id).toBe('acct_123');
      expect(result.data.status).toBe('active');
    });
  });

  describe('initiateConnectOnboarding', () => {
    it('initiates Connect onboarding with refresh and return URLs', async () => {
      const mockResponse = {
        account_type: 'standard' as const,
        url: 'https://connect.stripe.com/setup/s/123',
        stripe_account_id: 'acct_123',
      };
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });

      const result = await initiateConnectOnboarding(
        'http://test.lvh.me:5173/settings/payments',
        'http://test.lvh.me:5173/settings/payments/complete'
      );

      expect(apiClient.post).toHaveBeenCalledWith('/payments/connect/onboard/', {
        refresh_url: 'http://test.lvh.me:5173/settings/payments',
        return_url: 'http://test.lvh.me:5173/settings/payments/complete',
      });
      expect(result.data.url).toContain('stripe.com');
    });
  });

  describe('refreshConnectOnboardingLink', () => {
    it('refreshes Connect onboarding link', async () => {
      const mockResponse = {
        url: 'https://connect.stripe.com/setup/s/456',
      };
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });

      const result = await refreshConnectOnboardingLink(
        'http://test.lvh.me:5173/settings/payments',
        'http://test.lvh.me:5173/settings/payments/complete'
      );

      expect(apiClient.post).toHaveBeenCalledWith('/payments/connect/refresh-link/', {
        refresh_url: 'http://test.lvh.me:5173/settings/payments',
        return_url: 'http://test.lvh.me:5173/settings/payments/complete',
      });
      expect(result.data.url).toBe('https://connect.stripe.com/setup/s/456');
    });
  });

  describe('createAccountSession', () => {
    it('creates account session for embedded Connect', async () => {
      const mockResponse = {
        client_secret: 'acct_session_123',
        stripe_account_id: 'acct_123',
        publishable_key: 'pk_test_123',
      };
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });

      const result = await createAccountSession();

      expect(apiClient.post).toHaveBeenCalledWith('/payments/connect/account-session/');
      expect(result.data.client_secret).toBe('acct_session_123');
      expect(result.data.publishable_key).toBe('pk_test_123');
    });
  });

  describe('refreshConnectStatus', () => {
    it('refreshes Connect account status from Stripe', async () => {
      const mockConnectAccount = {
        id: 1,
        business: 1,
        business_name: 'Test Business',
        business_subdomain: 'test',
        stripe_account_id: 'acct_123',
        account_type: 'express' as const,
        status: 'active' as const,
        charges_enabled: true,
        payouts_enabled: true,
        details_submitted: true,
        onboarding_complete: true,
        onboarding_link: null,
        onboarding_link_expires_at: null,
        is_onboarding_link_valid: false,
        created_at: '2024-01-01T00:00:00Z',
        updated_at: '2024-01-02T00:00:00Z',
      };
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockConnectAccount });

      const result = await refreshConnectStatus();

      expect(apiClient.post).toHaveBeenCalledWith('/payments/connect/refresh-status/');
      expect(result.data.stripe_account_id).toBe('acct_123');
    });
  });

  // ============================================================================
  // Transactions
  // ============================================================================

  describe('getTransactions', () => {
    it('fetches transactions without filters', async () => {
      const mockResponse = {
        results: [],
        count: 0,
        page: 1,
        page_size: 20,
        total_pages: 0,
      };
      vi.mocked(apiClient.get).mockResolvedValue({ data: mockResponse });

      const result = await getTransactions();

      expect(apiClient.get).toHaveBeenCalledWith('/payments/transactions/');
      expect(result.data.results).toEqual([]);
    });

    it('builds query params correctly with all filters', async () => {
      const mockResponse = {
        results: [],
        count: 0,
        page: 2,
        page_size: 50,
        total_pages: 0,
      };
      vi.mocked(apiClient.get).mockResolvedValue({ data: mockResponse });

      await getTransactions({
        start_date: '2024-01-01',
        end_date: '2024-01-31',
        status: 'succeeded',
        transaction_type: 'payment',
        page: 2,
        page_size: 50,
      });

      expect(apiClient.get).toHaveBeenCalledWith(
        '/payments/transactions/?start_date=2024-01-01&end_date=2024-01-31&status=succeeded&transaction_type=payment&page=2&page_size=50'
      );
    });

    it('excludes status when set to "all"', async () => {
      vi.mocked(apiClient.get).mockResolvedValue({ data: { results: [], count: 0, page: 1, page_size: 20, total_pages: 0 } });

      await getTransactions({ status: 'all' });

      expect(apiClient.get).toHaveBeenCalledWith('/payments/transactions/');
    });

    it('excludes transaction_type when set to "all"', async () => {
      vi.mocked(apiClient.get).mockResolvedValue({ data: { results: [], count: 0, page: 1, page_size: 20, total_pages: 0 } });

      await getTransactions({ transaction_type: 'all' });

      expect(apiClient.get).toHaveBeenCalledWith('/payments/transactions/');
    });

    it('includes only date filters when provided', async () => {
      vi.mocked(apiClient.get).mockResolvedValue({ data: { results: [], count: 0, page: 1, page_size: 20, total_pages: 0 } });

      await getTransactions({
        start_date: '2024-01-01',
        end_date: '2024-01-31',
      });

      expect(apiClient.get).toHaveBeenCalledWith(
        '/payments/transactions/?start_date=2024-01-01&end_date=2024-01-31'
      );
    });

    it('includes only pagination params when provided', async () => {
      vi.mocked(apiClient.get).mockResolvedValue({ data: { results: [], count: 0, page: 3, page_size: 100, total_pages: 0 } });

      await getTransactions({
        page: 3,
        page_size: 100,
      });

      expect(apiClient.get).toHaveBeenCalledWith(
        '/payments/transactions/?page=3&page_size=100'
      );
    });

    it('returns transaction data correctly', async () => {
      const mockTransaction = {
        id: 1,
        business: 1,
        business_name: 'Test Business',
        stripe_payment_intent_id: 'pi_123',
        stripe_charge_id: 'ch_123',
        transaction_type: 'payment' as const,
        status: 'succeeded' as const,
        amount: 10000,
        amount_display: '$100.00',
        application_fee_amount: 300,
        fee_display: '$3.00',
        net_amount: 9700,
        currency: 'usd',
        customer_email: 'customer@example.com',
        customer_name: 'Test Customer',
        created_at: '2024-01-15T10:00:00Z',
        updated_at: '2024-01-15T10:00:00Z',
      };
      const mockResponse = {
        results: [mockTransaction],
        count: 1,
        page: 1,
        page_size: 20,
        total_pages: 1,
      };
      vi.mocked(apiClient.get).mockResolvedValue({ data: mockResponse });

      const result = await getTransactions();

      expect(result.data.results).toHaveLength(1);
      expect(result.data.results[0].stripe_payment_intent_id).toBe('pi_123');
    });
  });

  describe('getTransaction', () => {
    it('fetches single transaction by ID', async () => {
      const mockTransaction = {
        id: 1,
        business: 1,
        business_name: 'Test Business',
        stripe_payment_intent_id: 'pi_123',
        stripe_charge_id: 'ch_123',
        transaction_type: 'payment' as const,
        status: 'succeeded' as const,
        amount: 10000,
        amount_display: '$100.00',
        application_fee_amount: 300,
        fee_display: '$3.00',
        net_amount: 9700,
        currency: 'usd',
        customer_email: 'customer@example.com',
        customer_name: 'Test Customer',
        created_at: '2024-01-15T10:00:00Z',
        updated_at: '2024-01-15T10:00:00Z',
      };
      vi.mocked(apiClient.get).mockResolvedValue({ data: mockTransaction });

      const result = await getTransaction(1);

      expect(apiClient.get).toHaveBeenCalledWith('/payments/transactions/1/');
      expect(result.data.id).toBe(1);
      expect(result.data.stripe_payment_intent_id).toBe('pi_123');
    });
  });

  describe('getTransactionSummary', () => {
    it('fetches transaction summary without filters', async () => {
      const mockSummary = {
        total_transactions: 10,
        total_volume: 50000,
        total_volume_display: '$500.00',
        total_fees: 1500,
        total_fees_display: '$15.00',
        net_revenue: 48500,
        net_revenue_display: '$485.00',
        successful_transactions: 9,
        failed_transactions: 1,
        refunded_transactions: 0,
        average_transaction: 5000,
        average_transaction_display: '$50.00',
      };
      vi.mocked(apiClient.get).mockResolvedValue({ data: mockSummary });

      const result = await getTransactionSummary();

      expect(apiClient.get).toHaveBeenCalledWith('/payments/transactions/summary/');
      expect(result.data.total_transactions).toBe(10);
    });

    it('builds query params correctly with date filters', async () => {
      const mockSummary = {
        total_transactions: 5,
        total_volume: 25000,
        total_volume_display: '$250.00',
        total_fees: 750,
        total_fees_display: '$7.50',
        net_revenue: 24250,
        net_revenue_display: '$242.50',
        successful_transactions: 5,
        failed_transactions: 0,
        refunded_transactions: 0,
        average_transaction: 5000,
        average_transaction_display: '$50.00',
      };
      vi.mocked(apiClient.get).mockResolvedValue({ data: mockSummary });

      await getTransactionSummary({
        start_date: '2024-01-01',
        end_date: '2024-01-31',
      });

      expect(apiClient.get).toHaveBeenCalledWith(
        '/payments/transactions/summary/?start_date=2024-01-01&end_date=2024-01-31'
      );
    });

    it('includes only start_date when provided', async () => {
      vi.mocked(apiClient.get).mockResolvedValue({
        data: {
          total_transactions: 0,
          total_volume: 0,
          total_volume_display: '$0.00',
          total_fees: 0,
          total_fees_display: '$0.00',
          net_revenue: 0,
          net_revenue_display: '$0.00',
          successful_transactions: 0,
          failed_transactions: 0,
          refunded_transactions: 0,
          average_transaction: 0,
          average_transaction_display: '$0.00',
        }
      });

      await getTransactionSummary({ start_date: '2024-01-01' });

      expect(apiClient.get).toHaveBeenCalledWith(
        '/payments/transactions/summary/?start_date=2024-01-01'
      );
    });

    it('includes only end_date when provided', async () => {
      vi.mocked(apiClient.get).mockResolvedValue({
        data: {
          total_transactions: 0,
          total_volume: 0,
          total_volume_display: '$0.00',
          total_fees: 0,
          total_fees_display: '$0.00',
          net_revenue: 0,
          net_revenue_display: '$0.00',
          successful_transactions: 0,
          failed_transactions: 0,
          refunded_transactions: 0,
          average_transaction: 0,
          average_transaction_display: '$0.00',
        }
      });

      await getTransactionSummary({ end_date: '2024-01-31' });

      expect(apiClient.get).toHaveBeenCalledWith(
        '/payments/transactions/summary/?end_date=2024-01-31'
      );
    });
  });

  describe('getStripeCharges', () => {
    it('fetches Stripe charges with default limit', async () => {
      const mockResponse = {
        charges: [],
        has_more: false,
      };
      vi.mocked(apiClient.get).mockResolvedValue({ data: mockResponse });

      const result = await getStripeCharges();

      expect(apiClient.get).toHaveBeenCalledWith('/payments/transactions/charges/?limit=20');
      expect(result.data.charges).toEqual([]);
    });

    it('fetches Stripe charges with custom limit', async () => {
      const mockCharge = {
        id: 'ch_123',
        amount: 10000,
        amount_display: '$100.00',
        amount_refunded: 0,
        currency: 'usd',
        status: 'succeeded',
        paid: true,
        refunded: false,
        description: 'Test charge',
        receipt_email: 'test@example.com',
        receipt_url: 'https://stripe.com/receipt/123',
        created: 1704067200,
        payment_method_details: null,
        billing_details: null,
      };
      const mockResponse = {
        charges: [mockCharge],
        has_more: true,
      };
      vi.mocked(apiClient.get).mockResolvedValue({ data: mockResponse });

      const result = await getStripeCharges(50);

      expect(apiClient.get).toHaveBeenCalledWith('/payments/transactions/charges/?limit=50');
      expect(result.data.charges).toHaveLength(1);
      expect(result.data.has_more).toBe(true);
    });
  });

  describe('getStripePayouts', () => {
    it('fetches Stripe payouts with default limit', async () => {
      const mockResponse = {
        payouts: [],
        has_more: false,
      };
      vi.mocked(apiClient.get).mockResolvedValue({ data: mockResponse });

      const result = await getStripePayouts();

      expect(apiClient.get).toHaveBeenCalledWith('/payments/transactions/payouts/?limit=20');
      expect(result.data.payouts).toEqual([]);
    });

    it('fetches Stripe payouts with custom limit', async () => {
      const mockPayout = {
        id: 'po_123',
        amount: 50000,
        amount_display: '$500.00',
        currency: 'usd',
        status: 'paid',
        arrival_date: 1704153600,
        created: 1704067200,
        description: 'Test payout',
        destination: 'ba_123',
        failure_message: null,
        method: 'standard',
        type: 'bank_account',
      };
      const mockResponse = {
        payouts: [mockPayout],
        has_more: true,
      };
      vi.mocked(apiClient.get).mockResolvedValue({ data: mockResponse });

      const result = await getStripePayouts(100);

      expect(apiClient.get).toHaveBeenCalledWith('/payments/transactions/payouts/?limit=100');
      expect(result.data.payouts).toHaveLength(1);
      expect(result.data.has_more).toBe(true);
    });
  });

  describe('getStripeBalance', () => {
    it('fetches Stripe balance', async () => {
      const mockBalance = {
        available: [
          { amount: 10000, currency: 'usd', amount_display: '$100.00' },
        ],
        pending: [
          { amount: 5000, currency: 'usd', amount_display: '$50.00' },
        ],
        available_total: 10000,
        pending_total: 5000,
      };
      vi.mocked(apiClient.get).mockResolvedValue({ data: mockBalance });

      const result = await getStripeBalance();

      expect(apiClient.get).toHaveBeenCalledWith('/payments/transactions/balance/');
      expect(result.data.available_total).toBe(10000);
      expect(result.data.pending_total).toBe(5000);
    });
  });

  describe('exportTransactions', () => {
    it('exports transactions with CSV format', async () => {
      const mockBlob = new Blob(['csv,data'], { type: 'text/csv' });
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockBlob });

      const result = await exportTransactions({ format: 'csv' });

      expect(apiClient.post).toHaveBeenCalledWith(
        '/payments/transactions/export/',
        { format: 'csv' },
        { responseType: 'blob' }
      );
      expect(result.data).toBeInstanceOf(Blob);
    });

    it('exports transactions with all options', async () => {
      const mockBlob = new Blob(['excel,data'], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockBlob });

      await exportTransactions({
        format: 'xlsx',
        start_date: '2024-01-01',
        end_date: '2024-01-31',
        include_details: true,
      });

      expect(apiClient.post).toHaveBeenCalledWith(
        '/payments/transactions/export/',
        {
          format: 'xlsx',
          start_date: '2024-01-01',
          end_date: '2024-01-31',
          include_details: true,
        },
        { responseType: 'blob' }
      );
    });
  });

  // ============================================================================
  // Transaction Details & Refunds
  // ============================================================================

  describe('getTransactionDetail', () => {
    it('fetches detailed transaction information', async () => {
      const mockDetail = {
        id: 1,
        business: 1,
        business_name: 'Test Business',
        stripe_payment_intent_id: 'pi_123',
        stripe_charge_id: 'ch_123',
        transaction_type: 'payment' as const,
        status: 'succeeded' as const,
        amount: 10000,
        amount_display: '$100.00',
        application_fee_amount: 300,
        fee_display: '$3.00',
        net_amount: 9700,
        currency: 'usd',
        customer_email: 'customer@example.com',
        customer_name: 'Test Customer',
        created_at: '2024-01-15T10:00:00Z',
        updated_at: '2024-01-15T10:00:00Z',
        refunds: [],
        refundable_amount: 10000,
        total_refunded: 0,
        can_refund: true,
        payment_method_info: {
          type: 'card',
          brand: 'visa',
          last4: '4242',
          exp_month: 12,
          exp_year: 2025,
        },
        description: 'Test transaction',
      };
      vi.mocked(apiClient.get).mockResolvedValue({ data: mockDetail });

      const result = await getTransactionDetail(1);

      expect(apiClient.get).toHaveBeenCalledWith('/payments/transactions/1/');
      expect(result.data.refunds).toEqual([]);
      expect(result.data.can_refund).toBe(true);
      expect(result.data.payment_method_info?.brand).toBe('visa');
    });
  });

  describe('refundTransaction', () => {
    it('issues full refund without request object', async () => {
      const mockResponse = {
        success: true,
        refund_id: 're_123',
        amount: 10000,
        amount_display: '$100.00',
        status: 'succeeded',
        reason: null,
        transaction_status: 'refunded',
      };
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });

      const result = await refundTransaction(1);

      expect(apiClient.post).toHaveBeenCalledWith('/payments/transactions/1/refund/', {});
      expect(result.data.success).toBe(true);
      expect(result.data.refund_id).toBe('re_123');
    });

    it('issues partial refund with amount', async () => {
      const mockResponse = {
        success: true,
        refund_id: 're_456',
        amount: 5000,
        amount_display: '$50.00',
        status: 'succeeded',
        reason: null,
        transaction_status: 'partially_refunded',
      };
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });

      const result = await refundTransaction(1, { amount: 5000 });

      expect(apiClient.post).toHaveBeenCalledWith('/payments/transactions/1/refund/', {
        amount: 5000,
      });
      expect(result.data.amount).toBe(5000);
      expect(result.data.transaction_status).toBe('partially_refunded');
    });

    it('issues refund with reason and metadata', async () => {
      const mockResponse = {
        success: true,
        refund_id: 're_789',
        amount: 10000,
        amount_display: '$100.00',
        status: 'succeeded',
        reason: 'requested_by_customer',
        transaction_status: 'refunded',
      };
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });

      await refundTransaction(1, {
        reason: 'requested_by_customer',
        metadata: { support_ticket: 'TICKET-123' },
      });

      expect(apiClient.post).toHaveBeenCalledWith('/payments/transactions/1/refund/', {
        reason: 'requested_by_customer',
        metadata: { support_ticket: 'TICKET-123' },
      });
    });
  });

  // ============================================================================
  // Subscriptions
  // ============================================================================

  describe('getSubscriptionPlans', () => {
    it('fetches available subscription plans and add-ons', async () => {
      const mockPlan = {
        id: 1,
        name: 'Professional',
        description: 'For growing businesses',
        plan_type: 'base' as const,
        business_tier: 'professional',
        price_monthly: 4900,
        price_yearly: 49900,
        features: ['Feature 1', 'Feature 2'],
        permissions: { can_use_contracts: true },
        limits: { max_resources: 100 },
        transaction_fee_percent: 2.9,
        transaction_fee_fixed: 30,
        is_most_popular: true,
        show_price: true,
        stripe_price_id: 'price_123',
      };
      const mockResponse = {
        current_tier: 'free',
        current_plan: null,
        plans: [mockPlan],
        addons: [],
      };
      vi.mocked(apiClient.get).mockResolvedValue({ data: mockResponse });

      const result = await getSubscriptionPlans();

      expect(apiClient.get).toHaveBeenCalledWith('/payments/plans/');
      expect(result.data.plans).toHaveLength(1);
      expect(result.data.current_tier).toBe('free');
    });
  });

  describe('createCheckoutSession', () => {
    it('creates checkout session with monthly billing', async () => {
      const mockResponse = {
        checkout_url: 'https://checkout.stripe.com/session/123',
        session_id: 'cs_123',
      };
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });

      const result = await createCheckoutSession(1, 'monthly');

      expect(apiClient.post).toHaveBeenCalledWith('/payments/checkout/', {
        plan_id: 1,
        billing_period: 'monthly',
      });
      expect(result.data.checkout_url).toContain('stripe.com');
    });

    it('creates checkout session with yearly billing', async () => {
      const mockResponse = {
        checkout_url: 'https://checkout.stripe.com/session/456',
        session_id: 'cs_456',
      };
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });

      const result = await createCheckoutSession(2, 'yearly');

      expect(apiClient.post).toHaveBeenCalledWith('/payments/checkout/', {
        plan_id: 2,
        billing_period: 'yearly',
      });
      expect(result.data.session_id).toBe('cs_456');
    });

    it('defaults to monthly billing when not specified', async () => {
      const mockResponse = {
        checkout_url: 'https://checkout.stripe.com/session/789',
        session_id: 'cs_789',
      };
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });

      await createCheckoutSession(3);

      expect(apiClient.post).toHaveBeenCalledWith('/payments/checkout/', {
        plan_id: 3,
        billing_period: 'monthly',
      });
    });
  });

  describe('getSubscriptions', () => {
    it('fetches active subscriptions', async () => {
      const mockSubscription = {
        id: 'sub_123',
        plan_name: 'Professional',
        plan_type: 'base' as const,
        status: 'active' as const,
        current_period_start: '2024-01-01T00:00:00Z',
        current_period_end: '2024-02-01T00:00:00Z',
        cancel_at_period_end: false,
        cancel_at: null,
        canceled_at: null,
        amount: 4900,
        amount_display: '$49.00',
        interval: 'month' as const,
        stripe_subscription_id: 'sub_stripe_123',
      };
      const mockResponse = {
        subscriptions: [mockSubscription],
        has_active_subscription: true,
      };
      vi.mocked(apiClient.get).mockResolvedValue({ data: mockResponse });

      const result = await getSubscriptions();

      expect(apiClient.get).toHaveBeenCalledWith('/payments/subscriptions/');
      expect(result.data.subscriptions).toHaveLength(1);
      expect(result.data.has_active_subscription).toBe(true);
    });
  });

  describe('cancelSubscription', () => {
    it('cancels subscription at period end by default', async () => {
      const mockResponse = {
        success: true,
        message: 'Subscription will be canceled at period end',
        cancel_at_period_end: true,
        current_period_end: '2024-02-01T00:00:00Z',
      };
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });

      const result = await cancelSubscription('sub_123');

      expect(apiClient.post).toHaveBeenCalledWith('/payments/subscriptions/cancel/', {
        subscription_id: 'sub_123',
        immediate: false,
      });
      expect(result.data.cancel_at_period_end).toBe(true);
    });

    it('cancels subscription immediately when requested', async () => {
      const mockResponse = {
        success: true,
        message: 'Subscription canceled immediately',
        cancel_at_period_end: false,
        current_period_end: '2024-02-01T00:00:00Z',
      };
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });

      const result = await cancelSubscription('sub_123', true);

      expect(apiClient.post).toHaveBeenCalledWith('/payments/subscriptions/cancel/', {
        subscription_id: 'sub_123',
        immediate: true,
      });
      expect(result.data.success).toBe(true);
    });
  });

  describe('reactivateSubscription', () => {
    it('reactivates a subscription set to cancel', async () => {
      const mockResponse = {
        success: true,
        message: 'Subscription reactivated successfully',
      };
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });

      const result = await reactivateSubscription('sub_123');

      expect(apiClient.post).toHaveBeenCalledWith('/payments/subscriptions/reactivate/', {
        subscription_id: 'sub_123',
      });
      expect(result.data.success).toBe(true);
    });
  });
});
