<?php

namespace Tests\Feature\Api\Marketing;

use App\Models\Coupon;
use App\Models\Package;
use App\Models\Tenant;
use App\Models\User;
use App\Services\Paddle\PaddleDiscountService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Mockery;
use Tests\TestCase;

class CouponPreviewTest extends TestCase
{
    use RefreshDatabase;

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

        parent::tearDown();
    }

    public function test_guest_can_preview_coupon(): void
    {
        $package = Package::factory()->create([
            'paddle_price_id' => 'pri_test',
            'price' => 100,
        ]);

        $coupon = Coupon::factory()->create([
            'code' => 'SAVE20',
            'paddle_discount_id' => 'dsc_123',
            'per_customer_limit' => null,
        ]);
        $coupon->packages()->attach($package);

        $this->instance(PaddleDiscountService::class, Mockery::mock(PaddleDiscountService::class, function ($mock) {
            $mock->shouldReceive('previewDiscount')->andReturn([
                'totals' => [
                    'currency_code' => 'EUR',
                    'subtotal' => 10000,
                    'discount' => 2000,
                    'tax' => 0,
                    'total' => 8000,
                ],
            ]);
        }));

        $response = $this->postJson(route('api.v1.marketing.coupons.preview'), [
            'package_id' => $package->id,
            'code' => 'save20',
        ]);

        $response->assertOk()
            ->assertJsonPath('coupon.code', 'SAVE20');

        $this->assertEquals(80.0, (float) $response->json('pricing.total'));
    }

    public function test_invalid_coupon_returns_validation_error(): void
    {
        $package = Package::factory()->create([
            'paddle_price_id' => 'pri_test_invalid',
        ]);

        $this->postJson(route('api.v1.marketing.coupons.preview'), [
            'package_id' => $package->id,
            'code' => 'UNKNOWN',
        ])->assertUnprocessable()
            ->assertJsonValidationErrors('code');
    }

    public function test_coupon_with_per_customer_limit_requires_login(): void
    {
        $package = Package::factory()->create([
            'paddle_price_id' => 'pri_test_login',
        ]);

        $coupon = Coupon::factory()->create([
            'code' => 'LIMITED',
            'paddle_discount_id' => 'dsc_login',
            'per_customer_limit' => 1,
        ]);
        $coupon->packages()->attach($package);

        $response = $this->postJson(route('api.v1.marketing.coupons.preview'), [
            'package_id' => $package->id,
            'code' => 'LIMITED',
        ]);

        $response->assertUnprocessable()
            ->assertJsonValidationErrors('code');

        $this->assertSame(
            __('marketing.coupon.errors.login_required'),
            $response->json('errors.code.0')
        );
    }

    public function test_coupon_with_per_customer_limit_allows_logged_in_tenant(): void
    {
        $tenant = Tenant::factory()->create();
        $user = User::factory()->create([
            'tenant_id' => $tenant->id,
        ]);

        $package = Package::factory()->create([
            'paddle_price_id' => 'pri_test_logged_in',
            'price' => 120,
        ]);

        $coupon = Coupon::factory()->create([
            'code' => 'LIMITEDTENANT',
            'paddle_discount_id' => 'dsc_logged_in',
            'per_customer_limit' => 1,
        ]);
        $coupon->packages()->attach($package);

        $this->instance(PaddleDiscountService::class, Mockery::mock(PaddleDiscountService::class, function ($mock) {
            $mock->shouldReceive('previewDiscount')->andReturn([
                'totals' => [
                    'currency_code' => 'EUR',
                    'subtotal' => 12000,
                    'discount' => 2000,
                    'tax' => 0,
                    'total' => 10000,
                ],
            ]);
        }));

        $response = $this->actingAs($user)->postJson(route('api.v1.marketing.coupons.preview'), [
            'package_id' => $package->id,
            'code' => 'limitedtenant',
        ]);

        $response->assertOk()
            ->assertJsonPath('coupon.code', 'LIMITEDTENANT');

        $this->assertEquals(100.0, (float) $response->json('pricing.total'));
    }
}
