<?php

declare(strict_types=1);

use App\Models\Currency;
use App\Models\Plan;
use App\Models\User;
use App\Services\Billing\CurrencyService;
use Database\Seeders\RoleAndPermissionSeeder;
use Illuminate\Support\Facades\Cache;

beforeEach(function (): void {
    $this->seed(RoleAndPermissionSeeder::class);

    // Create base and secondary currencies
    Currency::query()->delete();
    Currency::factory()->base()->create();
    Currency::factory()->create([
        'code' => 'EUR',
        'symbol' => '€',
        'name' => 'Euro',
        'exchange_rate' => 0.920000,
        'is_enabled' => true,
    ]);
    Currency::factory()->create([
        'code' => 'GBP',
        'symbol' => '£',
        'name' => 'British Pound',
        'exchange_rate' => 0.790000,
        'is_enabled' => true,
    ]);
    Currency::factory()->disabled()->create([
        'code' => 'JPY',
        'symbol' => '¥',
        'name' => 'Japanese Yen',
        'exchange_rate' => 149.500000,
    ]);

    Cache::flush();
});

test('CurrencyService converts between currencies correctly', function (): void {
    $service = app(CurrencyService::class);

    // USD to EUR: 100 * 0.92 = 92.00
    $result = $service->convert(100.00, 'USD', 'EUR');
    expect($result)->toBe(92.00);

    // USD to GBP: 100 * 0.79 = 79.00
    $result = $service->convert(100.00, 'USD', 'GBP');
    expect($result)->toBe(79.00);

    // Same currency returns same amount
    $result = $service->convert(50.00, 'USD', 'USD');
    expect($result)->toBe(50.00);
});

test('CurrencyService converts from non-base currency', function (): void {
    $service = app(CurrencyService::class);

    // EUR to USD: 92 / 0.92 * 1.0 = 100.00
    $result = $service->convert(92.00, 'EUR', 'USD');
    expect($result)->toBe(100.00);

    // EUR to GBP: 92 / 0.92 * 0.79 = 79.00
    $result = $service->convert(92.00, 'EUR', 'GBP');
    expect($result)->toBe(79.00);
});

test('CurrencyService::getEnabledCurrencies returns only enabled currencies', function (): void {
    $service = app(CurrencyService::class);
    $currencies = $service->getEnabledCurrencies();

    expect($currencies)->toHaveCount(3); // USD, EUR, GBP (JPY is disabled)
    expect($currencies->pluck('code')->toArray())->not->toContain('JPY');
});

test('CurrencyService::getEnabledCurrencies is cached', function (): void {
    $service = app(CurrencyService::class);

    // First call
    $currencies = $service->getEnabledCurrencies();
    expect($currencies)->toHaveCount(3);

    // Cache should be set
    expect(Cache::has('currencies:enabled'))->toBeTrue();
});

test('CurrencyService::getBaseCurrency returns the base currency', function (): void {
    $service = app(CurrencyService::class);
    $base = $service->getBaseCurrency();

    expect($base->code)->toBe('USD');
    expect($base->is_base)->toBeTrue();
});

test('CurrencyService::formatPrice formats with correct symbol', function (): void {
    $service = app(CurrencyService::class);

    expect($service->formatPrice(99.99, 'USD'))->toBe('$99.99');
    expect($service->formatPrice(92.00, 'EUR'))->toBe('€92.00');
    expect($service->formatPrice(79.00, 'GBP'))->toBe('£79.00');
});

test('Plan::priceInCurrency converts plan price', function (): void {
    $plan = Plan::factory()->create(['price' => 100.00, 'currency' => 'USD']);

    $eur = $plan->priceInCurrency('EUR');
    expect($eur)->toBe(92.00);

    $usd = $plan->priceInCurrency('USD');
    expect($usd)->toBe(100.00);
});

test('Currency model scopes work correctly', function (): void {
    expect(Currency::enabled()->count())->toBe(3);
    expect(Currency::base()->count())->toBe(1);
});

test('admin can list currencies', function (): void {
    $admin = User::factory()->create();
    $admin->assignRole('admin');

    $response = $this->actingAs($admin)
        ->get('http://admin.ezscale.dev/currencies');

    $response->assertOk();
});

test('admin can create a currency', function (): void {
    $admin = User::factory()->create();
    $admin->assignRole('admin');

    $response = $this->actingAs($admin)
        ->post('http://admin.ezscale.dev/currencies', [
            'code' => 'CHF',
            'symbol' => 'Fr',
            'name' => 'Swiss Franc',
            'decimal_places' => 2,
            'exchange_rate' => 0.88,
            'is_base' => false,
            'is_enabled' => true,
        ]);

    $response->assertRedirect();

    $this->assertDatabaseHas('currencies', [
        'code' => 'CHF',
        'symbol' => 'Fr',
        'name' => 'Swiss Franc',
    ]);
});

test('admin can update a currency', function (): void {
    $admin = User::factory()->create();
    $admin->assignRole('admin');

    $currency = Currency::where('code', 'EUR')->first();

    $response = $this->actingAs($admin)
        ->put("http://admin.ezscale.dev/currencies/{$currency->id}", [
            'symbol' => '€',
            'name' => 'Euro (Updated)',
            'decimal_places' => 2,
            'exchange_rate' => 0.95,
            'is_base' => false,
            'is_enabled' => true,
        ]);

    $response->assertRedirect();

    $this->assertDatabaseHas('currencies', [
        'id' => $currency->id,
        'name' => 'Euro (Updated)',
    ]);
});

test('admin cannot delete base currency', function (): void {
    $admin = User::factory()->create();
    $admin->assignRole('admin');

    $base = Currency::where('is_base', true)->first();

    $response = $this->actingAs($admin)
        ->delete("http://admin.ezscale.dev/currencies/{$base->id}");

    $response->assertRedirect();
    $this->assertDatabaseHas('currencies', ['id' => $base->id]);
});

test('admin can delete non-base currency', function (): void {
    $admin = User::factory()->create();
    $admin->assignRole('admin');

    $currency = Currency::where('code', 'GBP')->first();

    $response = $this->actingAs($admin)
        ->delete("http://admin.ezscale.dev/currencies/{$currency->id}");

    $response->assertRedirect();
    $this->assertDatabaseMissing('currencies', ['id' => $currency->id]);
});

test('user model has currency field', function (): void {
    $user = User::factory()->create(['currency' => 'EUR']);

    expect($user->currency)->toBe('EUR');
});
