<?php

namespace Tests\Tenant;

use App\Facades\Settings;
use Carbon\Carbon;
use Database\Seeders\Tenant\FinanceSettingSeeder;
use Database\Seeders\Tenant\SantanderFinanceSettingSeeder;
use Illuminate\Http\Client\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Http;
use Mtc\MercuryDataModels\Finance\Config\SantanderConfig;
use Mtc\MercuryDataModels\Finance\Contracts\FinanceRequestData;
use Mtc\MercuryDataModels\Finance\FinanceServiceHelper;
use Mtc\MercuryDataModels\Finance\Santander\Models\SantanderProduct;
use Mtc\MercuryDataModels\Finance\Services\SantanderFinance;
use Tests\TenantTestCase;

class SantanderFinanceServiceTest extends TenantTestCase
{
    protected function setUp(): void
    {
        parent::setUp();

        // Clear any cached products and truncate tables
        Cache::flush();
        $this->clearSantanderTables();
    }

    protected function clearSantanderTables(): void
    {
        SantanderProduct::truncate();
    }

    protected function seedAndConfigureSantander(string $environment = 'uat'): void
    {
        $this->seed(FinanceSettingSeeder::class);
        $this->seed(SantanderFinanceSettingSeeder::class);

        // Disable other providers to ensure Santander is selected
        Settings::update('finance-codeweavers-enabled', false);
        Settings::update('finance-ivendi-enabled', false);
        Settings::update('finance-evolution-enabled', false);

        // Global credentials via config
        Config::set('services.santander.username', 'test-user');
        Config::set('services.santander.password', 'test-pass');
        Config::set('services.santander.environment', $environment);

        // Per-tenant settings
        Settings::update('finance-santander-enabled', true);
        Settings::update('finance-santander-dealer-code', 'TEST123');
    }

    protected function makeFinanceRequest(): FinanceRequestData
    {
        return new FinanceRequestData(
            uuid: 'abc123',
            registration_number: 'SD12 ABC',
            cap_id: '1231122',
            cap_code: 'FDAB123456',
            dealer_id: 'abc123',
            engine_size: 1999,
            condition: 'new',
            registration_date: Carbon::create(2020, 1, 1),
            term: 48,
            mileage: 100000,
            annual_mileage: 10000,
            price: 25000,
            deposit: 5000,
            credit_rating: 'excellent',
        );
    }

    public function testPcpQuoteCalculation(): void
    {
        Http::fake([
            '*products*' => Http::response($this->singlePcpProductsResponse()),
            '*multi*' => Http::response($this->successResponse(1)),
            '*' => Http::response([], 400),
        ]);

        $this->seedAndConfigureSantander();

        $config = new SantanderConfig();
        $config->clearProductsCache();

        $provider = FinanceServiceHelper::initializeForSite();

        $this->assertInstanceOf(SantanderFinance::class, $provider);

        $result = $provider->calculate($this->makeFinanceRequest());

        Http::assertSent(function (Request $request) {
            return str_contains($request->url(), 'ficresuktestcert.santanderconsumer.com/QuotingTool/api/multi')
                && $request['ThirdPartyUser'] === 'test-user'
                && $request['ThirdPartyPassword'] === 'test-pass'
                && isset($request['QuoteRequestList'])
                && $request['QuoteRequestList'][0]['DealerCode'] === 'TEST123'
                && $request['QuoteRequestList'][0]['ProductId'] === 'PCP001'
                && $request['QuoteRequestList'][0]['CapCode'] === 'FDAB123456'
                && $request['QuoteRequestList'][0]['CostPrice'] == 25000
                && $request['QuoteRequestList'][0]['Period'] == 48
                && $request['QuoteRequestList'][0]['Deposit'] == 5000;
        });

        $this->assertEquals(1, $result->count());
        $finance = $result->first();
        $this->assertEquals(350.00, $finance->monthly_price);
        $this->assertEquals(48, $finance->term);
        $this->assertEquals(9.9, $finance->apr);
        $this->assertEquals(5000, $finance->total_deposit);
        $this->assertEquals(25000, $finance->cash_price);
        $this->assertEquals('santander', $finance->provider);
        $this->assertEquals('Santander PCP', $finance->product_name);
    }

    private function singlePcpProductsResponse(): array
    {
        return [
            'financeProduct' => [
                'product' => [
                    [
                        'productId' => 'PCP001',
                        'productTypeId' => 11,
                        'productDesc' => 'Personal Contract Purchase',
                    ],
                ],
            ],
        ];
    }

    public function testLiveEnvironmentUrl(): void
    {
        Http::fake([
            'https://ficresuk.santanderconsumer.com/*' => Http::response($this->singlePcpProductsResponse()),
            '*multi*' => Http::response($this->successResponse(1)),
            '*' => Http::response([], 400),
        ]);

        $this->seedAndConfigureSantander('live');

        $config = new SantanderConfig();
        $config->clearProductsCache();

        $provider = FinanceServiceHelper::initializeForSite();
        $provider->calculate($this->makeFinanceRequest());

        Http::assertSent(function (Request $request) {
            return str_contains($request->url(), 'ficresuk.santanderconsumer.com');
        });
    }

    public function testServiceUnavailable(): void
    {
        Http::fake([
            'https://ficresuktestcert.santanderconsumer.com/*' => Http::response([], 500),
            '*' => Http::response([], 400),
        ]);

        $this->seedAndConfigureSantander();

        $config = new SantanderConfig();
        $config->clearProductsCache();

        $provider = FinanceServiceHelper::initializeForSite();
        $result = $provider->calculate($this->makeFinanceRequest());

        $this->assertEmpty($result);
    }

    public function testNoQuotesAvailable(): void
    {
        Http::fake([
            '*products*' => Http::response($this->singlePcpProductsResponse()),
            '*multi*' => Http::response($this->emptyResponse()),
            '*' => Http::response([], 400),
        ]);

        $this->seedAndConfigureSantander();

        $config = new SantanderConfig();
        $config->clearProductsCache();

        $provider = FinanceServiceHelper::initializeForSite();
        $result = $provider->calculate($this->makeFinanceRequest());

        $this->assertEmpty($result);
    }

    public function testQuoteStatusNotSuccessful(): void
    {
        Http::fake([
            '*products*' => Http::response($this->singlePcpProductsResponse()),
            '*multi*' => Http::response($this->failedQuoteResponse()),
            '*' => Http::response([], 400),
        ]);

        $this->seedAndConfigureSantander();

        $config = new SantanderConfig();
        $config->clearProductsCache();

        $provider = FinanceServiceHelper::initializeForSite();
        $result = $provider->calculate($this->makeFinanceRequest());

        $this->assertEmpty($result);
    }

    public function testConfigReadsFromConfigAndSettings(): void
    {
        $this->seed(SantanderFinanceSettingSeeder::class);

        // Global config
        Config::set('services.santander.username', 'my-username');
        Config::set('services.santander.password', 'my-password');
        Config::set('services.santander.environment', 'live');

        // Per-tenant settings
        Settings::update('finance-santander-enabled', true);
        Settings::update('finance-santander-dealer-code', 'DEALER001');

        $config = new SantanderConfig();

        $this->assertTrue($config->enabled());
        $this->assertEquals('my-username', $config->username());
        $this->assertEquals('my-password', $config->password());
        $this->assertEquals('DEALER001', $config->dealerCode());
        $this->assertEquals('live', $config->environment());
        $this->assertEquals('https://ficresuk.santanderconsumer.com', $config->baseUrl());
    }

    public function testConfigUatEnvironment(): void
    {
        $this->seed(SantanderFinanceSettingSeeder::class);
        Config::set('services.santander.environment', 'uat');

        $config = new SantanderConfig();

        $this->assertEquals('https://ficresuktestcert.santanderconsumer.com', $config->baseUrl());
    }

    public function testAllProductTypesInSingleRequest(): void
    {
        Http::fake([
            '*products*' => Http::response($this->productsApiResponse()),
            '*multi*' => Http::response($this->multiProductResponse()),
            '*' => Http::response([], 400),
        ]);

        $this->seedAndConfigureSantander();

        $config = new SantanderConfig();
        $config->clearProductsCache();

        $provider = FinanceServiceHelper::initializeForSite();
        $result = $provider->calculate($this->makeFinanceRequest());

        // Verify quote request was made with all products
        Http::assertSent(function (Request $request) {
            if (!str_contains($request->url(), '/QuotingTool/api/multi')) {
                return false;
            }
            $quotes = $request['QuoteRequestList'];
            return count($quotes) === 5;
        });

        // Should return 5 results
        $this->assertEquals(5, $result->count());

        // Verify each product type
        $this->assertNotNull($result->get('CS'));
        $this->assertEquals('Santander CS', $result->get('CS')->product_name);

        $this->assertNotNull($result->get('LP'));
        $this->assertEquals('Santander LP', $result->get('LP')->product_name);

        $this->assertNotNull($result->get('DP'));
        $this->assertEquals('Santander DP', $result->get('DP')->product_name);

        $this->assertNotNull($result->get('PCP'));
        $this->assertEquals('Santander PCP', $result->get('PCP')->product_name);

        $this->assertNotNull($result->get('DPCP'));
        $this->assertEquals('Santander DPCP', $result->get('DPCP')->product_name);
    }

    public function testNoRequestWhenNoProductsFromApi(): void
    {
        Http::fake([
            '*products*' => Http::response($this->emptyProductsResponse()),
            '*' => Http::response($this->successResponse(1)),
        ]);

        $this->seedAndConfigureSantander();

        $config = new SantanderConfig();
        $config->clearProductsCache();

        $provider = FinanceServiceHelper::initializeForSite();
        $result = $provider->calculate($this->makeFinanceRequest());

        // Should have made 1 request to products API
        Http::assertSent(function (Request $request) {
            return str_contains($request->url(), '/QuotingTool/api/products');
        });

        // Should NOT have made quote request since no products
        Http::assertNotSent(function (Request $request) {
            return str_contains($request->url(), '/QuotingTool/api/multi');
        });

        $this->assertEmpty($result);
    }

    public function testFetchProductsFromApi(): void
    {
        Http::fake([
            '*products*' => Http::response($this->productsApiResponse()),
            '*multi*' => Http::response($this->multiProductResponse()),
        ]);

        $this->seedAndConfigureSantander();

        $config = new SantanderConfig();
        $config->clearProductsCache();

        $provider = FinanceServiceHelper::initializeForSite();
        $result = $provider->calculate($this->makeFinanceRequest());

        // Should have made request to products API first
        Http::assertSent(function (Request $request) {
            return str_contains($request->url(), '/QuotingTool/api/products')
                && $request['ThirdPartyUser'] === 'test-user'
                && $request['ThirdPartyPassword'] === 'test-pass'
                && $request['DealerProduct']['DealerCode'] === 'TEST123';
        });

        // Then should have made quote request with fetched products
        Http::assertSent(function (Request $request) {
            return str_contains($request->url(), '/QuotingTool/api/multi');
        });

        // Should return results for the fetched products
        $this->assertGreaterThan(0, $result->count());

        // Verify products were stored in database
        $this->assertEquals(5, SantanderProduct::count());
    }

    public function testProductsSyncCaching(): void
    {
        Http::fake([
            '*products*' => Http::response($this->productsApiResponse()),
            '*multi*' => Http::response($this->successResponse(1)),
        ]);

        $this->seedAndConfigureSantander();

        $config = new SantanderConfig();
        $config->clearProductsCache();

        // First call - should hit API and store products
        $synced1 = $config->ensureProductsSynced();
        $this->assertTrue($synced1);
        $this->assertEquals(5, SantanderProduct::count());

        // Second call - should use cache (no additional HTTP call to products API)
        $synced2 = $config->ensureProductsSynced();
        $this->assertTrue($synced2);

        // Products API should only be called once
        $productsCalls = 0;
        Http::assertSent(function (Request $request) use (&$productsCalls) {
            if (str_contains($request->url(), '/QuotingTool/api/products')) {
                $productsCalls++;
            }
            return true;
        });
        $this->assertEquals(1, $productsCalls);
    }

    public function testParseProductsResponseWithDifferentCasing(): void
    {
        $this->seedAndConfigureSantander();

        $config = new SantanderConfig();

        // Test with camelCase (as in documentation)
        $reflection = new \ReflectionClass($config);
        $method = $reflection->getMethod('parseAndStoreProducts');
        $method->setAccessible(true);

        $camelCaseResponse = [
            'financeProduct' => [
                'product' => [
                    ['productId' => 'P001', 'productTypeId' => 11],
                    ['productId' => 'P002', 'productTypeId' => 2],
                ],
            ],
        ];

        $result = $method->invoke($config, $camelCaseResponse);
        $this->assertTrue($result);
        $this->assertEquals(2, SantanderProduct::count());
        $this->assertNotNull(SantanderProduct::where('product_id', 'P001')->first());
        $this->assertNotNull(SantanderProduct::where('product_id', 'P002')->first());

        // Clear for next test
        $this->clearSantanderTables();

        // Test with PascalCase
        $pascalCaseResponse = [
            'FinanceProduct' => [
                'Product' => [
                    ['ProductId' => 'P003', 'ProductTypeId' => 4],
                ],
            ],
        ];

        $result = $method->invoke($config, $pascalCaseResponse);
        $this->assertTrue($result);
        $this->assertEquals(1, SantanderProduct::count());
        $this->assertNotNull(SantanderProduct::where('product_id', 'P003')->first());
    }

    public function testClearProductsCache(): void
    {
        $this->seedAndConfigureSantander();

        $config = new SantanderConfig();
        $cacheKey = 'santander-products-synced-' . tenant('id') . '-TEST123';

        // Put something in cache
        Cache::put($cacheKey, true, 86400);
        $this->assertTrue(Cache::has($cacheKey));

        // Clear cache
        $config->clearProductsCache();
        $this->assertFalse(Cache::has($cacheKey));
    }

    public function testRefreshProducts(): void
    {
        // Use sequence to return different responses on subsequent calls
        Http::fake([
            '*products*' => Http::sequence()
                ->push($this->singlePcpProductsResponse())
                ->push($this->productsApiResponse()),
            '*' => Http::response([], 400),
        ]);

        $this->seedAndConfigureSantander();

        $config = new SantanderConfig();

        // First sync - returns 1 product
        $config->ensureProductsSynced();
        $this->assertEquals(1, SantanderProduct::count());

        // Refresh should clear cache and fetch new products - returns 5 products
        $config->refreshProducts();
        $this->assertEquals(5, SantanderProduct::count());
    }

    public function testLowestRateProductSelected(): void
    {
        // Return two PCP products with different rates
        Http::fake([
            '*products*' => Http::response([
                'financeProduct' => [
                    'product' => [
                        [
                            'productId' => 'PCP001',
                            'productTypeId' => 11,
                            'defaultRate' => 9.9,
                        ],
                        [
                            'productId' => 'PCP002',
                            'productTypeId' => 11,
                            'defaultRate' => 7.5, // Lower rate - should be selected
                        ],
                    ],
                ],
            ]),
            '*multi*' => Http::response($this->successResponse(1)),
        ]);

        $this->seedAndConfigureSantander();

        $config = new SantanderConfig();
        $config->clearProductsCache();

        $provider = FinanceServiceHelper::initializeForSite();
        $result = $provider->calculate($this->makeFinanceRequest());

        // Should have selected the product with lower rate (PCP002)
        Http::assertSent(function (Request $request) {
            if (!str_contains($request->url(), '/QuotingTool/api/multi')) {
                return false;
            }
            $quotes = $request['QuoteRequestList'];
            // Should only have 1 quote (best rate for PCP)
            return count($quotes) === 1 && $quotes[0]['ProductId'] === 'PCP002';
        });
    }

    private function successResponse(int $quoteId): array
    {
        return [
            'quoteResponseList' => [
                [
                    'quoteId' => $quoteId,
                    'quoteStatus' => 1,
                    'period' => 48,
                    'apr' => 9.9,
                    'rate' => 8.5,
                    'deposit' => 5000,
                    'costPrice' => 25000,
                    'totalAmount' => 21800,
                    'ballon' => 8000,
                    'paf' => 10,
                    'bff' => 0,
                    'payments' => [
                        [
                            'months' => 1,
                            'value' => 350.00,
                        ],
                        [
                            'months' => 47,
                            'value' => 350.00,
                        ],
                    ],
                ],
            ],
        ];
    }

    private function multiProductResponse(): array
    {
        return [
            'quoteResponseList' => [
                [
                    'quoteId' => 1,
                    'quoteStatus' => 1,
                    'period' => 48,
                    'apr' => 9.9,
                    'rate' => 8.5,
                    'deposit' => 5000,
                    'costPrice' => 25000,
                    'totalAmount' => 21800,
                    'ballon' => 0,
                    'paf' => 10,
                    'bff' => 0,
                    'payments' => [['months' => 48, 'value' => 420.00]],
                ],
                [
                    'quoteId' => 2,
                    'quoteStatus' => 1,
                    'period' => 48,
                    'apr' => 8.9,
                    'rate' => 7.5,
                    'deposit' => 5000,
                    'costPrice' => 25000,
                    'totalAmount' => 20800,
                    'ballon' => 0,
                    'paf' => 10,
                    'bff' => 0,
                    'payments' => [['months' => 48, 'value' => 400.00]],
                ],
                [
                    'quoteId' => 3,
                    'quoteStatus' => 1,
                    'period' => 48,
                    'apr' => 10.9,
                    'rate' => 9.5,
                    'deposit' => 5000,
                    'costPrice' => 25000,
                    'totalAmount' => 22800,
                    'ballon' => 0,
                    'paf' => 10,
                    'bff' => 0,
                    'payments' => [['months' => 48, 'value' => 440.00]],
                ],
                [
                    'quoteId' => 4,
                    'quoteStatus' => 1,
                    'period' => 48,
                    'apr' => 9.9,
                    'rate' => 8.5,
                    'deposit' => 5000,
                    'costPrice' => 25000,
                    'totalAmount' => 21800,
                    'ballon' => 8000,
                    'paf' => 10,
                    'bff' => 0,
                    'payments' => [['months' => 48, 'value' => 350.00]],
                ],
                [
                    'quoteId' => 5,
                    'quoteStatus' => 1,
                    'period' => 48,
                    'apr' => 9.5,
                    'rate' => 8.0,
                    'deposit' => 5000,
                    'costPrice' => 25000,
                    'totalAmount' => 21500,
                    'ballon' => 7000,
                    'paf' => 10,
                    'bff' => 0,
                    'payments' => [['months' => 48, 'value' => 360.00]],
                ],
            ],
        ];
    }

    private function emptyResponse(): array
    {
        return [
            'quoteResponseList' => [],
        ];
    }

    private function failedQuoteResponse(): array
    {
        return [
            'quoteResponseList' => [
                [
                    'quoteId' => 1,
                    'quoteStatus' => 0,
                    'errorMessage' => 'Vehicle not eligible for finance',
                ],
            ],
        ];
    }

    private function productsApiResponse(): array
    {
        return [
            'financeProduct' => [
                'product' => [
                    [
                        'productId' => 'API-CS-001',
                        'productTypeId' => 2,
                        'productDesc' => 'Conditional Sale',
                    ],
                    [
                        'productId' => 'API-LP-001',
                        'productTypeId' => 4,
                        'productDesc' => 'Lease Purchase',
                    ],
                    [
                        'productId' => 'API-DP-001',
                        'productTypeId' => 6,
                        'productDesc' => 'Deferred Payment',
                    ],
                    [
                        'productId' => 'API-PCP-001',
                        'productTypeId' => 11,
                        'productDesc' => 'Personal Contract Purchase',
                    ],
                    [
                        'productId' => 'API-DPCP-001',
                        'productTypeId' => 12,
                        'productDesc' => 'Deferred Personal Contract Purchase',
                    ],
                ],
            ],
        ];
    }

    private function emptyProductsResponse(): array
    {
        return [
            'financeProduct' => [
                'product' => [],
            ],
        ];
    }
}
