<?php

namespace Tests\Feature;

use App\Models\Category;
use App\Models\Network;
use App\Models\Plan;
use App\Models\PlanVendorMapping;
use App\Models\User;
use App\Models\Vendor;
use App\Models\Wallet;
use App\Services\Providers\Contracts\ProviderInterface;
use App\Services\Providers\ProviderFactory;
use App\Services\Providers\ProviderResponse;
use App\Services\TransactionService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Mockery;
use Tests\TestCase;

class TransactionRefundOnFailureTest extends TestCase
{
    use RefreshDatabase;

    public function test_transaction_is_refunded_when_vendor_purchase_fails(): void
    {
        $user = User::factory()->create();
        Wallet::create(['user_id' => $user->id, 'balance' => 5000]);

        $category = Category::create(['name' => 'Data SME', 'slug' => 'data_sme']);
        $network = Network::create([
            'name' => 'MTN',
            'slug' => 'mtn',
            'vendor_network_code_json' => ['msorg' => ['data' => '1']],
        ]);
        $plan = Plan::create([
            'network_id' => $network->id,
            'category_id' => $category->id,
            'name' => 'MTN SME 1GB',
            'value' => '1GB',
            'selling_price' => 500,
            'status' => 'available',
            'sort_order' => 1,
        ]);
        $vendor = Vendor::create([
            'name' => 'Failing Vendor',
            'type' => 'msorg',
            'base_url' => 'https://vendor.example.com',
            'auth_type' => 'token',
            'secret_encrypted' => 'abc123',
            'status' => 'active',
            'timeout_seconds' => 30,
        ]);
        PlanVendorMapping::create([
            'plan_id' => $plan->id,
            'vendor_id' => $vendor->id,
            'vendor_plan_code' => 'MTN-SME-1GB',
            'priority' => 1,
            'status' => 'active',
        ]);

        $providerFactory = Mockery::mock(ProviderFactory::class);
        $providerFactory->shouldReceive('forVendor')->andReturn(new class implements ProviderInterface {
            public function getBalance(?\App\Models\Transaction $transaction = null): array { return ['success' => true]; }
            public function getPlans(?\App\Models\Transaction $transaction = null): array { return []; }
            public function purchaseData(array $payload, ?\App\Models\Transaction $transaction = null): ProviderResponse
            {
                return ProviderResponse::failed('Purchase failed', ['status' => 'failed'], hardFailure: false);
            }
            public function purchaseAirtime(array $payload, ?\App\Models\Transaction $transaction = null): ProviderResponse
            {
                return ProviderResponse::failed('Not used');
            }
            public function purchaseCable(array $payload, ?\App\Models\Transaction $transaction = null): ProviderResponse
            {
                return ProviderResponse::failed('Not used');
            }
            public function validateIuc(array $payload, ?\App\Models\Transaction $transaction = null): array { return []; }
            public function purchaseElectricity(array $payload, ?\App\Models\Transaction $transaction = null): ProviderResponse
            {
                return ProviderResponse::failed('Not used');
            }
            public function validateMeter(array $payload, ?\App\Models\Transaction $transaction = null): array { return []; }
            public function purchaseEpin(array $payload, ?\App\Models\Transaction $transaction = null): ProviderResponse
            {
                return ProviderResponse::failed('Not used');
            }
            public function queryTransaction(array $payload, ?\App\Models\Transaction $transaction = null): ProviderResponse
            {
                return ProviderResponse::failed('Not used');
            }
        });
        $this->app->instance(ProviderFactory::class, $providerFactory);

        /** @var TransactionService $transactionService */
        $transactionService = $this->app->make(TransactionService::class);

        $transaction = $transactionService->purchase($user, [
            'category_slug' => 'data_sme',
            'plan_id' => $plan->id,
            'phone' => '08030000000',
            'mobile_number' => '08030000000',
            'internal_reference' => 'TX-FAIL-REFUND-001',
        ]);

        $this->assertSame('reversed', $transaction->status);
        $this->assertDatabaseHas('wallet_ledgers', [
            'reference' => 'wallet:purchase:TX-FAIL-REFUND-001',
            'entry_type' => 'debit',
        ]);
        $this->assertDatabaseHas('wallet_ledgers', [
            'reference' => 'wallet:refund:TX-FAIL-REFUND-001',
            'entry_type' => 'credit',
        ]);
        $this->assertSame('5000.00', (string) $user->fresh()->wallet->balance);
    }

    public function test_failed_transaction_does_not_store_negative_profit_without_confirmed_vendor_debit(): void
    {
        $user = User::factory()->create();
        Wallet::create(['user_id' => $user->id, 'balance' => 5000]);

        $category = Category::create(['name' => 'Data SME', 'slug' => 'data_sme']);
        $network = Network::create([
            'name' => 'MTN',
            'slug' => 'mtn',
            'vendor_network_code_json' => ['msorg' => ['data' => '1']],
        ]);
        $plan = Plan::create([
            'network_id' => $network->id,
            'category_id' => $category->id,
            'name' => 'MTN SME 500MB',
            'value' => '500MB',
            'selling_price' => 98,
            'status' => 'available',
            'sort_order' => 1,
        ]);
        $vendor = Vendor::create([
            'name' => 'Failing Vendor',
            'type' => 'msorg',
            'base_url' => 'https://vendor.example.com',
            'auth_type' => 'token',
            'secret_encrypted' => 'abc123',
            'status' => 'active',
            'timeout_seconds' => 30,
        ]);
        PlanVendorMapping::create([
            'plan_id' => $plan->id,
            'vendor_id' => $vendor->id,
            'vendor_plan_code' => 'MTN-SME-500MB',
            'priority' => 1,
            'status' => 'active',
        ]);

        $providerFactory = Mockery::mock(ProviderFactory::class);
        $providerFactory->shouldReceive('forVendor')->andReturn(new class implements ProviderInterface {
            public function getBalance(?\App\Models\Transaction $transaction = null): array { return ['success' => true]; }
            public function getPlans(?\App\Models\Transaction $transaction = null): array { return []; }
            public function purchaseData(array $payload, ?\App\Models\Transaction $transaction = null): ProviderResponse
            {
                return ProviderResponse::failed(
                    message: 'Purchase failed',
                    raw: ['status' => 'failed', 'price' => 99],
                    vendorCost: 99,
                    hardFailure: false
                );
            }
            public function purchaseAirtime(array $payload, ?\App\Models\Transaction $transaction = null): ProviderResponse { return ProviderResponse::failed('Not used'); }
            public function purchaseCable(array $payload, ?\App\Models\Transaction $transaction = null): ProviderResponse { return ProviderResponse::failed('Not used'); }
            public function validateIuc(array $payload, ?\App\Models\Transaction $transaction = null): array { return []; }
            public function purchaseElectricity(array $payload, ?\App\Models\Transaction $transaction = null): ProviderResponse { return ProviderResponse::failed('Not used'); }
            public function validateMeter(array $payload, ?\App\Models\Transaction $transaction = null): array { return []; }
            public function purchaseEpin(array $payload, ?\App\Models\Transaction $transaction = null): ProviderResponse { return ProviderResponse::failed('Not used'); }
            public function queryTransaction(array $payload, ?\App\Models\Transaction $transaction = null): ProviderResponse { return ProviderResponse::failed('Not used'); }
        });
        $this->app->instance(ProviderFactory::class, $providerFactory);

        /** @var TransactionService $transactionService */
        $transactionService = $this->app->make(TransactionService::class);

        $transaction = $transactionService->purchase($user, [
            'category_slug' => 'data_sme',
            'plan_id' => $plan->id,
            'phone' => '08030000000',
            'mobile_number' => '08030000000',
            'internal_reference' => 'TX-FAIL-PROFIT-001',
        ])->fresh();

        $this->assertSame('reversed', $transaction->status);
        $this->assertSame('0.00', (string) $transaction->profit);
        $this->assertFalse((bool) data_get((array) $transaction->meta_json, 'vendor_debit_confirmed', false));
    }

    public function test_failed_transaction_keeps_negative_profit_when_vendor_debit_is_confirmed(): void
    {
        $user = User::factory()->create();
        Wallet::create(['user_id' => $user->id, 'balance' => 5000]);

        $category = Category::create(['name' => 'Data SME', 'slug' => 'data_sme']);
        $network = Network::create([
            'name' => 'MTN',
            'slug' => 'mtn',
            'vendor_network_code_json' => ['msorg' => ['data' => '1']],
        ]);
        $plan = Plan::create([
            'network_id' => $network->id,
            'category_id' => $category->id,
            'name' => 'MTN SME 500MB',
            'value' => '500MB',
            'selling_price' => 98,
            'status' => 'available',
            'sort_order' => 1,
        ]);
        $vendor = Vendor::create([
            'name' => 'Failing Vendor',
            'type' => 'msorg',
            'base_url' => 'https://vendor.example.com',
            'auth_type' => 'token',
            'secret_encrypted' => 'abc123',
            'status' => 'active',
            'timeout_seconds' => 30,
        ]);
        PlanVendorMapping::create([
            'plan_id' => $plan->id,
            'vendor_id' => $vendor->id,
            'vendor_plan_code' => 'MTN-SME-500MB',
            'priority' => 1,
            'status' => 'active',
        ]);

        $providerFactory = Mockery::mock(ProviderFactory::class);
        $providerFactory->shouldReceive('forVendor')->andReturn(new class implements ProviderInterface {
            public function getBalance(?\App\Models\Transaction $transaction = null): array { return ['success' => true]; }
            public function getPlans(?\App\Models\Transaction $transaction = null): array { return []; }
            public function purchaseData(array $payload, ?\App\Models\Transaction $transaction = null): ProviderResponse
            {
                return ProviderResponse::failed(
                    message: 'Purchase failed',
                    raw: [
                        'status' => 'failed',
                        'price' => 99,
                        'balance_before' => 1000,
                        'balance_after' => 901,
                    ],
                    vendorCost: 99,
                    hardFailure: false
                );
            }
            public function purchaseAirtime(array $payload, ?\App\Models\Transaction $transaction = null): ProviderResponse { return ProviderResponse::failed('Not used'); }
            public function purchaseCable(array $payload, ?\App\Models\Transaction $transaction = null): ProviderResponse { return ProviderResponse::failed('Not used'); }
            public function validateIuc(array $payload, ?\App\Models\Transaction $transaction = null): array { return []; }
            public function purchaseElectricity(array $payload, ?\App\Models\Transaction $transaction = null): ProviderResponse { return ProviderResponse::failed('Not used'); }
            public function validateMeter(array $payload, ?\App\Models\Transaction $transaction = null): array { return []; }
            public function purchaseEpin(array $payload, ?\App\Models\Transaction $transaction = null): ProviderResponse { return ProviderResponse::failed('Not used'); }
            public function queryTransaction(array $payload, ?\App\Models\Transaction $transaction = null): ProviderResponse { return ProviderResponse::failed('Not used'); }
        });
        $this->app->instance(ProviderFactory::class, $providerFactory);

        /** @var TransactionService $transactionService */
        $transactionService = $this->app->make(TransactionService::class);

        $transaction = $transactionService->purchase($user, [
            'category_slug' => 'data_sme',
            'plan_id' => $plan->id,
            'phone' => '08030000000',
            'mobile_number' => '08030000000',
            'internal_reference' => 'TX-FAIL-PROFIT-002',
        ])->fresh();

        $this->assertSame('reversed', $transaction->status);
        $this->assertSame('-1.00', (string) $transaction->profit);
        $this->assertTrue((bool) data_get((array) $transaction->meta_json, 'vendor_debit_confirmed', false));
    }
}
