import { Test, TestingModule } from '@nestjs/testing';
import { ConfigService } from '@nestjs/config';
import { StripeService } from './stripe.service';

// Mock Stripe
jest.mock('stripe', () => {
    return jest.fn().mockImplementation(() => ({
        customers: {
            create: jest.fn().mockResolvedValue({ id: 'cus_test123', email: 'test@example.com' }),
        },
        subscriptions: {
            create: jest.fn().mockResolvedValue({ id: 'sub_test123', status: 'active' }),
            cancel: jest.fn().mockResolvedValue({ id: 'sub_test123', status: 'canceled' }),
            list: jest.fn().mockResolvedValue({ data: [] }),
        },
        checkout: {
            sessions: {
                create: jest.fn().mockResolvedValue({ id: 'cs_test123', url: 'https://checkout.stripe.com/test' }),
            },
        },
        billingPortal: {
            sessions: {
                create: jest.fn().mockResolvedValue({ id: 'bps_test123', url: 'https://billing.stripe.com/test' }),
            },
        },
        webhooks: {
            constructEvent: jest.fn().mockReturnValue({ type: 'checkout.session.completed' }),
        },
    }));
});

describe('StripeService', () => {
    let service: StripeService;
    let configService: ConfigService;

    beforeEach(async () => {
        const module: TestingModule = await Test.createTestingModule({
            providers: [
                StripeService,
                {
                    provide: ConfigService,
                    useValue: {
                        get: jest.fn((key: string) => {
                            const config: Record<string, string> = {
                                STRIPE_SECRET_KEY: 'sk_test_fake123',
                                STRIPE_WEBHOOK_SECRET: 'whsec_test123',
                            };
                            return config[key];
                        }),
                    },
                },
            ],
        }).compile();

        service = module.get<StripeService>(StripeService);
        configService = module.get<ConfigService>(ConfigService);

        // Initialize the service
        service.onModuleInit();
    });

    describe('onModuleInit', () => {
        it('should initialize Stripe client when secret key is configured', () => {
            expect(service.getClient()).toBeDefined();
        });

        it('should warn when STRIPE_SECRET_KEY is not configured', () => {
            const warnSpy = jest.spyOn(console, 'warn').mockImplementation();
            jest.spyOn(configService, 'get').mockReturnValue(undefined);

            const newService = new StripeService(configService);
            newService.onModuleInit();

            expect(warnSpy).toHaveBeenCalledWith('STRIPE_SECRET_KEY not configured');
            warnSpy.mockRestore();
        });
    });

    describe('createCustomer', () => {
        it('should create a customer with email and name', async () => {
            const result = await service.createCustomer('test@example.com', 'Test User');

            expect(result).toEqual({ id: 'cus_test123', email: 'test@example.com' });
            expect(service.getClient().customers.create).toHaveBeenCalledWith({
                email: 'test@example.com',
                name: 'Test User',
            });
        });
    });

    describe('createSubscription', () => {
        it('should create a subscription for a customer', async () => {
            const result = await service.createSubscription('cus_test123', 'price_test123');

            expect(result).toEqual({ id: 'sub_test123', status: 'active' });
            expect(service.getClient().subscriptions.create).toHaveBeenCalledWith({
                customer: 'cus_test123',
                items: [{ price: 'price_test123' }],
                payment_behavior: 'default_incomplete',
                expand: ['latest_invoice.payment_intent'],
            });
        });
    });

    describe('cancelSubscription', () => {
        it('should cancel a subscription', async () => {
            const result = await service.cancelSubscription('sub_test123');

            expect(result).toEqual({ id: 'sub_test123', status: 'canceled' });
        });
    });

    describe('listSubscriptions', () => {
        it('should list subscriptions for a customer', async () => {
            const result = await service.listSubscriptions('cus_test123');

            expect(result).toEqual({ data: [] });
            expect(service.getClient().subscriptions.list).toHaveBeenCalledWith({
                customer: 'cus_test123',
                status: 'all',
            });
        });
    });

    describe('createCheckoutSession', () => {
        it('should create a checkout session', async () => {
            const result = await service.createCheckoutSession(
                'cus_test123',
                'price_test123',
                'https://example.com/success',
                'https://example.com/cancel',
            );

            expect(result).toEqual({ id: 'cs_test123', url: 'https://checkout.stripe.com/test' });
        });
    });

    describe('createBillingPortalSession', () => {
        it('should create a billing portal session', async () => {
            const result = await service.createBillingPortalSession(
                'cus_test123',
                'https://example.com/return',
            );

            expect(result).toEqual({ id: 'bps_test123', url: 'https://billing.stripe.com/test' });
        });
    });

    describe('constructWebhookEvent', () => {
        it('should construct a webhook event from request body', () => {
            const body = Buffer.from('{}');
            const signature = 'test_signature';

            const result = service.constructWebhookEvent(body, signature);

            expect(result).toEqual({ type: 'checkout.session.completed' });
        });
    });
});
