<?php

namespace Tests\Feature;

use App\Models\Package;
use App\Models\Tenant;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Schema;
use Mockery;
use Tests\TestCase;
use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;

#[RunTestsInSeparateProcesses]
class CheckoutPaymentIntentTest extends TestCase
{
    use RefreshDatabase;

    protected function tearDown(): void
    {
        Mockery::close();
        parent::tearDown();
    }

    private function actingAsTenantUser(): User
    {
        $user = User::factory()->create();
        Tenant::factory()->create(['user_id' => $user->id]);
        Auth::login($user);

        return $user;
    }

    public function test_returns_null_client_secret_for_free_package(): void
    {
        $this->actingAsTenantUser();
        $package = Package::factory()->create([
            'price' => 0,
        ]);

        if (Schema::hasColumn('packages', 'is_free')) {
            \DB::table('packages')->where('id', $package->id)->update(['is_free' => true]);
        }

        $response = $this->postJson('/stripe/create-payment-intent', [
            'package_id' => $package->id,
        ]);

        $response->assertOk();

        if (Schema::hasColumn('packages', 'is_free')) {
            $response->assertJson([
                'client_secret' => null,
                'free_package' => true,
            ]);
        } else {
            $response->assertJson([
                'client_secret' => null,
            ]);
        }
    }

    private function mockStripePaymentIntent(object $payload): void
    {
        if (class_exists(\Stripe\PaymentIntent::class, false)) {
            $this->fail('Stripe\\PaymentIntent already loaded; unable to mock static methods.');
        }

        $mock = Mockery::mock('alias:Stripe\PaymentIntent');
        $mock->shouldReceive('create')
            ->once()
            ->andReturn($payload);
    }

    private function mockStripePaymentIntentFailure(\Throwable $exception): void
    {
        if (class_exists(\Stripe\PaymentIntent::class, false)) {
            $this->fail('Stripe\\PaymentIntent already loaded; unable to mock static methods.');
        }

        $mock = Mockery::mock('alias:Stripe\PaymentIntent');
        $mock->shouldReceive('create')
            ->once()
            ->andThrow($exception);
    }

    public function test_creates_payment_intent_and_returns_client_secret(): void
    {
        config(['services.stripe.secret' => 'sk_test_dummy']);

        $this->actingAsTenantUser();
        $package = Package::factory()->create([
            'price' => 129,
        ]);

        $this->mockStripePaymentIntent((object) [
            'id' => 'pi_test_123',
            'client_secret' => 'secret_test_456',
        ]);

        $response = $this->postJson('/stripe/create-payment-intent', [
            'package_id' => $package->id,
        ]);

        $response->assertOk()
            ->assertJson([
                'client_secret' => 'secret_test_456',
            ]);
    }

    public function test_returns_error_when_payment_intent_creation_fails(): void
    {
        config(['services.stripe.secret' => 'sk_test_dummy']);

        $this->actingAsTenantUser();
        $package = Package::factory()->create([
            'price' => 59,
        ]);

        $this->mockStripePaymentIntentFailure(new \RuntimeException('Stripe failure'));

        $response = $this->postJson('/stripe/create-payment-intent', [
            'package_id' => $package->id,
        ]);

        $response->assertStatus(500)
            ->assertJson([
                'error' => 'Fehler beim Erstellen der Zahlungsdaten: Stripe failure',
            ]);
    }
}
