<?php

declare(strict_types=1);

use App\Events\SubscriptionCancelled;
use App\Models\CancellationSurvey;
use App\Models\Plan;
use App\Models\User;
use App\Services\Billing\BillingServiceFactory;
use App\Services\Billing\BillingServiceInterface;
use Database\Seeders\RoleAndPermissionSeeder;

beforeEach(function (): void {
    $this->seed(RoleAndPermissionSeeder::class);
    $this->accountUrl = 'http://'.config('app.domains.account');
});

it('persists cancellation survey with reason, feedback, and would_return', function (): void {
    Event::fake([SubscriptionCancelled::class]);

    $user = User::factory()->customer()->create();
    $plan = Plan::factory()->create();
    $subscription = $user->subscriptions()->create([
        'type' => 'default',
        'stripe_id' => 'sub_test_'.uniqid(),
        'stripe_status' => 'active',
        'stripe_price' => 'price_test',
        'plan_id' => $plan->id,
    ]);

    $mockService = Mockery::mock(BillingServiceInterface::class);
    $mockService->shouldReceive('cancelSubscription')->once()->andReturn(true);

    $mockFactory = Mockery::mock(BillingServiceFactory::class);
    $mockFactory->shouldReceive('make')->with('stripe')->andReturn($mockService);
    $this->app->instance(BillingServiceFactory::class, $mockFactory);

    $this->actingAs($user)
        ->post($this->accountUrl."/subscriptions/{$subscription->id}/cancel", [
            'immediately' => false,
            'reason' => 'Too expensive',
            'feedback' => 'The price increased too much for my budget.',
            'would_return' => 'maybe',
        ])
        ->assertRedirect();

    $survey = CancellationSurvey::where('subscription_id', $subscription->id)->first();

    expect($survey)->not->toBeNull();
    expect($survey->user_id)->toBe($user->id);
    expect($survey->subscription_id)->toBe($subscription->id);
    expect($survey->cancellation_reason)->toBe('Too expensive');
    expect($survey->cancellation_feedback)->toBe('The price increased too much for my budget.');
    expect($survey->would_return)->toBe('maybe');
    expect($survey->created_at)->not->toBeNull();

    Event::assertDispatched(SubscriptionCancelled::class, function ($event) use ($user, $subscription): bool {
        return $event->user->id === $user->id
            && $event->subscription->id === $subscription->id
            && $event->cancellationReason === 'Too expensive';
    });
});

it('persists cancellation survey without optional fields (backward compat)', function (): void {
    Event::fake([SubscriptionCancelled::class]);

    $user = User::factory()->customer()->create();
    $plan = Plan::factory()->create();
    $subscription = $user->subscriptions()->create([
        'type' => 'default',
        'stripe_id' => 'sub_test_'.uniqid(),
        'stripe_status' => 'active',
        'stripe_price' => 'price_test',
        'plan_id' => $plan->id,
    ]);

    $mockService = Mockery::mock(BillingServiceInterface::class);
    $mockService->shouldReceive('cancelSubscription')->once()->andReturn(true);

    $mockFactory = Mockery::mock(BillingServiceFactory::class);
    $mockFactory->shouldReceive('make')->with('stripe')->andReturn($mockService);
    $this->app->instance(BillingServiceFactory::class, $mockFactory);

    $this->actingAs($user)
        ->post($this->accountUrl."/subscriptions/{$subscription->id}/cancel", [
            'immediately' => true,
        ])
        ->assertRedirect();

    $survey = CancellationSurvey::where('subscription_id', $subscription->id)->first();

    expect($survey)->not->toBeNull();
    expect($survey->cancellation_reason)->toBe('');
    expect($survey->cancellation_feedback)->toBeNull();
    expect($survey->would_return)->toBeNull();

    Event::assertDispatched(SubscriptionCancelled::class, function ($event): bool {
        return $event->cancellationReason === null;
    });
});

it('creates survey belonging to the correct user and subscription', function (): void {
    Event::fake([SubscriptionCancelled::class]);

    $user = User::factory()->customer()->create();
    $otherUser = User::factory()->customer()->create();
    $plan = Plan::factory()->create();

    $subscription = $user->subscriptions()->create([
        'type' => 'default',
        'stripe_id' => 'sub_test_'.uniqid(),
        'stripe_status' => 'active',
        'stripe_price' => 'price_test',
        'plan_id' => $plan->id,
    ]);

    $otherSubscription = $otherUser->subscriptions()->create([
        'type' => 'default',
        'stripe_id' => 'sub_test_'.uniqid(),
        'stripe_status' => 'active',
        'stripe_price' => 'price_test',
        'plan_id' => $plan->id,
    ]);

    $mockService = Mockery::mock(BillingServiceInterface::class);
    $mockService->shouldReceive('cancelSubscription')->once()->andReturn(true);

    $mockFactory = Mockery::mock(BillingServiceFactory::class);
    $mockFactory->shouldReceive('make')->with('stripe')->andReturn($mockService);
    $this->app->instance(BillingServiceFactory::class, $mockFactory);

    $this->actingAs($user)
        ->post($this->accountUrl."/subscriptions/{$subscription->id}/cancel", [
            'reason' => 'No longer needed',
            'would_return' => 'yes',
        ])
        ->assertRedirect();

    $survey = CancellationSurvey::first();

    expect($survey->user_id)->toBe($user->id);
    expect($survey->subscription_id)->toBe($subscription->id);

    // Verify relationships load correctly
    expect($survey->user->id)->toBe($user->id);
    expect($survey->subscription->id)->toBe($subscription->id);

    // No survey for the other user
    expect(CancellationSurvey::where('user_id', $otherUser->id)->count())->toBe(0);
});

it('does not create survey when cancellation fails', function (): void {
    Event::fake([SubscriptionCancelled::class]);

    $user = User::factory()->customer()->create();
    $plan = Plan::factory()->create();
    $subscription = $user->subscriptions()->create([
        'type' => 'default',
        'stripe_id' => 'sub_test_'.uniqid(),
        'stripe_status' => 'active',
        'stripe_price' => 'price_test',
        'plan_id' => $plan->id,
    ]);

    $mockService = Mockery::mock(BillingServiceInterface::class);
    $mockService->shouldReceive('cancelSubscription')->once()->andReturn(false);

    $mockFactory = Mockery::mock(BillingServiceFactory::class);
    $mockFactory->shouldReceive('make')->with('stripe')->andReturn($mockService);
    $this->app->instance(BillingServiceFactory::class, $mockFactory);

    $this->actingAs($user)
        ->post($this->accountUrl."/subscriptions/{$subscription->id}/cancel", [
            'reason' => 'Too expensive',
            'feedback' => 'Price is too high.',
            'would_return' => 'no',
        ])
        ->assertRedirect();

    expect(CancellationSurvey::count())->toBe(0);
    Event::assertNotDispatched(SubscriptionCancelled::class);
});
