<?php

declare(strict_types=1);

use App\Models\Plan;
use App\Models\PlanConfigGroup;
use App\Models\PlanConfigOption;
use App\Models\PlanConfigValue;
use App\Models\User;
use App\Services\Billing\BillingServiceFactory;
use App\Services\Billing\BillingServiceInterface;
use Database\Seeders\RoleAndPermissionSeeder;

beforeEach(function (): void {
    $this->seed(RoleAndPermissionSeeder::class);
    $this->accountUrl = 'http://'.config('app.domains.account');

    // Mock the billing service so checkout pages don't hit Stripe
    $mockService = Mockery::mock(BillingServiceInterface::class);
    $mockService->shouldReceive('getPaymentMethods')->andReturn([]);

    $mockFactory = Mockery::mock(BillingServiceFactory::class);
    $mockFactory->shouldReceive('make')->with('stripe')->andReturn($mockService);
    $this->app->instance(BillingServiceFactory::class, $mockFactory);
});

describe('Preset Plan Checkout', function (): void {
    it('loads config groups on checkout page', function (): void {
        $user = User::factory()->customer()->create();
        $plan = Plan::factory()->create(['service_type' => 'dedicated', 'status' => 'active']);

        $group = PlanConfigGroup::factory()->create([
            'name' => 'Test Network',
            'mode' => 'preset',
            'service_type' => 'dedicated',
            'is_active' => true,
        ]);
        $group->plans()->attach($plan);

        $option = PlanConfigOption::factory()->create([
            'group_id' => $group->id,
            'name' => 'Port Speed',
            'type' => 'radio',
            'is_active' => true,
        ]);

        PlanConfigValue::factory()->create([
            'option_id' => $option->id,
            'label' => '1Gbit',
            'value' => '1gbit',
            'monthly_price' => 0,
            'is_default' => true,
        ]);

        PlanConfigValue::factory()->create([
            'option_id' => $option->id,
            'label' => '10Gbit',
            'value' => '10gbit',
            'monthly_price' => 85.00,
        ]);

        // Mock createSetupIntent to avoid Stripe calls
        $user->createOrGetStripeCustomer = null;

        $response = $this->actingAs($user)
            ->get($this->accountUrl."/checkout/{$plan->id}");

        $response->assertOk();
        $response->assertInertia(fn ($page) => $page
            ->component('Checkout/Show')
            ->has('configGroups', 1)
            ->where('configGroups.0.name', 'Test Network')
            ->has('configGroups.0.options', 1)
            ->where('configGroups.0.options.0.name', 'Port Speed')
            ->has('configGroups.0.options.0.values', 2)
        );
    });

    it('excludes inactive options from checkout page', function (): void {
        $user = User::factory()->customer()->create();
        $plan = Plan::factory()->create(['service_type' => 'dedicated', 'status' => 'active']);

        $group = PlanConfigGroup::factory()->create([
            'mode' => 'preset',
            'is_active' => true,
        ]);
        $group->plans()->attach($plan);

        PlanConfigOption::factory()->create([
            'group_id' => $group->id,
            'name' => 'Active Option',
            'is_active' => true,
        ]);

        PlanConfigOption::factory()->create([
            'group_id' => $group->id,
            'name' => 'Inactive Option',
            'is_active' => false,
        ]);

        $response = $this->actingAs($user)
            ->get($this->accountUrl."/checkout/{$plan->id}");

        $response->assertOk();
        $response->assertInertia(fn ($page) => $page
            ->component('Checkout/Show')
            ->has('configGroups', 1)
            ->has('configGroups.0.options', 1)
            ->where('configGroups.0.options.0.name', 'Active Option')
        );
    });

    it('excludes inactive groups from checkout page', function (): void {
        $user = User::factory()->customer()->create();
        $plan = Plan::factory()->create(['service_type' => 'dedicated', 'status' => 'active']);

        $activeGroup = PlanConfigGroup::factory()->create([
            'name' => 'Active Group',
            'mode' => 'preset',
            'is_active' => true,
        ]);
        $activeGroup->plans()->attach($plan);

        PlanConfigOption::factory()->create([
            'group_id' => $activeGroup->id,
            'name' => 'Some Option',
            'is_active' => true,
        ]);

        $inactiveGroup = PlanConfigGroup::factory()->create([
            'name' => 'Inactive Group',
            'mode' => 'preset',
            'is_active' => false,
        ]);
        $inactiveGroup->plans()->attach($plan);

        PlanConfigOption::factory()->create([
            'group_id' => $inactiveGroup->id,
            'name' => 'Hidden Option',
            'is_active' => true,
        ]);

        $response = $this->actingAs($user)
            ->get($this->accountUrl."/checkout/{$plan->id}");

        $response->assertOk();
        $response->assertInertia(fn ($page) => $page
            ->component('Checkout/Show')
            ->has('configGroups', 1)
            ->where('configGroups.0.name', 'Active Group')
        );
    });
});

describe('BYO Checkout', function (): void {
    it('loads configurator for VPS service type', function (): void {
        $user = User::factory()->customer()->create();

        // Create the internal custom plan
        $plan = Plan::factory()->create([
            'slug' => 'vps-custom',
            'service_type' => 'vps',
            'status' => 'internal',
            'price' => 0,
        ]);

        $group = PlanConfigGroup::factory()->buildYourOwn('vps')->create([
            'name' => 'VPS Builder',
            'is_active' => true,
        ]);

        PlanConfigOption::factory()->slider(2.00, 0.003)->create([
            'group_id' => $group->id,
            'name' => 'CPU Cores',
            'provisioning_key' => 'cpu_cores',
            'min_qty' => 1,
            'max_qty' => 16,
            'unit_label' => 'cores',
        ]);

        PlanConfigOption::factory()->slider(1.00, 0.0015)->create([
            'group_id' => $group->id,
            'name' => 'RAM',
            'provisioning_key' => 'ram_gb',
            'min_qty' => 1,
            'max_qty' => 64,
            'unit_label' => 'GB',
        ]);

        $response = $this->actingAs($user)
            ->get($this->accountUrl.'/checkout/custom/vps');

        $response->assertOk();
        $response->assertInertia(fn ($page) => $page
            ->component('Checkout/Show')
            ->has('configGroups', 1)
            ->where('configGroups.0.name', 'VPS Builder')
            ->where('configGroups.0.mode', 'build_your_own')
            ->has('configGroups.0.options', 2)
            ->where('mode', 'custom')
        );
    });

    it('loads configurator for MySQL service type', function (): void {
        $user = User::factory()->customer()->create();

        Plan::factory()->create([
            'slug' => 'mysql-custom',
            'service_type' => 'mysql',
            'status' => 'internal',
            'price' => 0,
        ]);

        $group = PlanConfigGroup::factory()->buildYourOwn('mysql')->create([
            'name' => 'MySQL Builder',
            'is_active' => true,
        ]);

        PlanConfigOption::factory()->slider(0.20, 0.0003)->create([
            'group_id' => $group->id,
            'name' => 'Storage',
            'provisioning_key' => 'storage_gb',
        ]);

        $response = $this->actingAs($user)
            ->get($this->accountUrl.'/checkout/custom/mysql');

        $response->assertOk();
        $response->assertInertia(fn ($page) => $page
            ->component('Checkout/Show')
            ->where('mode', 'custom')
            ->has('configGroups', 1)
            ->where('configGroups.0.mode', 'build_your_own')
        );
    });

    it('returns 404 for unsupported service type', function (): void {
        $user = User::factory()->customer()->create();

        $response = $this->actingAs($user)
            ->get($this->accountUrl.'/checkout/custom/dedicated');

        $response->assertNotFound();
    });

    it('returns 404 when no BYO group exists for service type', function (): void {
        $user = User::factory()->customer()->create();

        // Create the custom plan but no BYO group
        Plan::factory()->create([
            'slug' => 'game-custom',
            'service_type' => 'game',
            'status' => 'internal',
            'price' => 0,
        ]);

        $response = $this->actingAs($user)
            ->get($this->accountUrl.'/checkout/custom/game');

        $response->assertNotFound();
    });
});

describe('Authentication', function (): void {
    it('redirects unauthenticated user from preset checkout', function (): void {
        $plan = Plan::factory()->create(['status' => 'active']);

        $response = $this->get($this->accountUrl."/checkout/{$plan->id}");

        $response->assertRedirect();
    });

    it('redirects unauthenticated user from BYO checkout', function (): void {
        $response = $this->get($this->accountUrl.'/checkout/custom/vps');

        $response->assertRedirect();
    });
});

describe('Seeder Integration', function (): void {
    it('seeds all BYO groups with correct options', function (): void {
        $this->seed(\Database\Seeders\PlanSeeder::class);
        $this->seed(\Database\Seeders\ConfigOptionSeeder::class);

        // VPS Builder
        $vpsBuilder = PlanConfigGroup::where('name', 'VPS Builder')->first();
        expect($vpsBuilder)->not->toBeNull();
        expect($vpsBuilder->mode)->toBe('build_your_own');
        expect($vpsBuilder->service_type)->toBe('vps');
        expect($vpsBuilder->options)->toHaveCount(3);

        $cpuOption = $vpsBuilder->options->firstWhere('name', 'CPU Cores');
        expect($cpuOption->type)->toBe('slider');
        expect($cpuOption->provisioning_key)->toBe('cpu_cores');
        expect($cpuOption->min_qty)->toBe(1);
        expect($cpuOption->max_qty)->toBe(16);
        expect((float) $cpuOption->monthly_price)->toBe(2.00);
        expect((float) $cpuOption->hourly_price)->toBe(0.003);

        // MySQL Builder
        $mysqlBuilder = PlanConfigGroup::where('name', 'MySQL Builder')->first();
        expect($mysqlBuilder)->not->toBeNull();
        expect($mysqlBuilder->options)->toHaveCount(2);

        // Game Server Builder
        $gameBuilder = PlanConfigGroup::where('name', 'Game Server Builder')->first();
        expect($gameBuilder)->not->toBeNull();
        expect($gameBuilder->options)->toHaveCount(3);
    });

    it('seeds preset groups with correct values and plan attachments', function (): void {
        $this->seed(\Database\Seeders\PlanSeeder::class);
        $this->seed(\Database\Seeders\ConfigOptionSeeder::class);

        // Dedicated RAM - DDR4 attached to 4 servers
        $ramGroup = PlanConfigGroup::where('name', 'Dedicated RAM - DDR4')->first();
        expect($ramGroup)->not->toBeNull();
        expect($ramGroup->plans)->toHaveCount(4);

        $ramOption = $ramGroup->options->first();
        expect($ramOption->name)->toBe('DDR4 ECC RAM');
        expect($ramOption->type)->toBe('dropdown');
        expect($ramOption->required)->toBeTrue();
        expect($ramOption->values)->toHaveCount(8);

        // Check the 32 GB default value
        $defaultValue = $ramOption->values->firstWhere('label', '32 GB');
        expect($defaultValue->is_default)->toBeTrue();
        expect((float) $defaultValue->monthly_price)->toBe(0.00);

        // Check the 512 GB value
        $maxValue = $ramOption->values->firstWhere('label', '512 GB');
        expect((float) $maxValue->monthly_price)->toBe(120.00);

        // Verify plan slugs are attached
        $attachedSlugs = $ramGroup->plans->pluck('slug')->sort()->values()->toArray();
        expect($attachedSlugs)->toBe(['dell-r440', 'dell-r540', 'dell-r640', 'dell-r740']);
    });

    it('applies correct discount pricing for billing cycles', function (): void {
        $this->seed(\Database\Seeders\PlanSeeder::class);
        $this->seed(\Database\Seeders\ConfigOptionSeeder::class);

        // Check a value with $25 monthly (Semi-Managed from Server Management)
        $mgmtGroup = PlanConfigGroup::where('name', 'Server Management')->first();
        $mgmtOption = $mgmtGroup->options->first();
        $semiManaged = $mgmtOption->values->firstWhere('value', 'semi_managed');

        // quarterly = 25 * 3 * 0.95 = 71.25
        expect((float) $semiManaged->quarterly_price)->toBe(71.25);
        // semi_annual = 25 * 6 * 0.90 = 135.00
        expect((float) $semiManaged->semi_annual_price)->toBe(135.00);
        // annual = 25 * 12 * 0.85 = 255.00
        expect((float) $semiManaged->annual_price)->toBe(255.00);
    });

    it('is idempotent when run multiple times', function (): void {
        $this->seed(\Database\Seeders\PlanSeeder::class);
        $this->seed(\Database\Seeders\ConfigOptionSeeder::class);
        $this->seed(\Database\Seeders\ConfigOptionSeeder::class);

        // Should not create duplicates
        expect(PlanConfigGroup::where('name', 'VPS Builder')->count())->toBe(1);
        expect(PlanConfigGroup::where('name', 'Dedicated RAM - DDR4')->count())->toBe(1);
        expect(PlanConfigGroup::where('name', 'M.2 NVMe')->count())->toBe(1);

        // Options should not be duplicated
        $vpsBuilder = PlanConfigGroup::where('name', 'VPS Builder')->first();
        expect($vpsBuilder->options)->toHaveCount(3);
    });

    it('seeds Dedicated Network group with radio and dropdown options', function (): void {
        $this->seed(\Database\Seeders\PlanSeeder::class);
        $this->seed(\Database\Seeders\ConfigOptionSeeder::class);

        $networkGroup = PlanConfigGroup::where('name', 'Dedicated Network')->first();
        expect($networkGroup)->not->toBeNull();
        expect($networkGroup->options)->toHaveCount(3);

        $portSpeed = $networkGroup->options->firstWhere('name', 'Network Port Speed');
        expect($portSpeed->type)->toBe('radio');
        expect($portSpeed->required)->toBeTrue();
        expect($portSpeed->values)->toHaveCount(5);

        $publicBw = $networkGroup->options->firstWhere('name', 'Public Bandwidth');
        expect($publicBw->type)->toBe('dropdown');
        expect($publicBw->values)->toHaveCount(4);

        $privateBw = $networkGroup->options->firstWhere('name', 'Private Bandwidth');
        expect($privateBw->type)->toBe('radio');
        expect($privateBw->values)->toHaveCount(2);
    });

    it('seeds M.2 NVMe group attached to 14th gen servers', function (): void {
        $this->seed(\Database\Seeders\PlanSeeder::class);
        $this->seed(\Database\Seeders\ConfigOptionSeeder::class);

        $nvmeGroup = PlanConfigGroup::where('name', 'M.2 NVMe')->first();
        expect($nvmeGroup)->not->toBeNull();
        expect($nvmeGroup->plans)->toHaveCount(4);

        $option = $nvmeGroup->options->first();
        expect($option->type)->toBe('dropdown');
        expect($option->values)->toHaveCount(4);

        // Check that 2x 2TB is the most expensive at $75
        $maxValue = $option->values->firstWhere('label', '2x 2TB');
        expect((float) $maxValue->monthly_price)->toBe(75.00);
    });

    it('seeds VPS Add-ons with quantity and checkbox options', function (): void {
        $this->seed(\Database\Seeders\PlanSeeder::class);
        $this->seed(\Database\Seeders\ConfigOptionSeeder::class);

        $addonsGroup = PlanConfigGroup::where('name', 'VPS Add-ons')->first();
        expect($addonsGroup)->not->toBeNull();
        expect($addonsGroup->options)->toHaveCount(2);

        $ipv4 = $addonsGroup->options->firstWhere('name', 'IPv4 Addresses');
        expect($ipv4->type)->toBe('quantity');
        expect($ipv4->min_qty)->toBe(1);
        expect($ipv4->max_qty)->toBe(8);
        expect((float) $ipv4->monthly_price)->toBe(3.00);

        $windows = $addonsGroup->options->firstWhere('name', 'Windows License');
        expect($windows->type)->toBe('checkbox');
        expect($windows->values)->toHaveCount(1);
    });
});
