<?php

declare(strict_types=1);

use App\Models\AuditLog;
use App\Models\Coupon;
use App\Models\CouponRedemption;
use App\Models\Invoice;
use App\Models\Plan;
use App\Models\Service;
use App\Models\Setting;
use App\Models\User;
use Database\Seeders\RoleAndPermissionSeeder;

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

// ---------------------------------------------------------------------------
// Dashboard
// ---------------------------------------------------------------------------
describe('Dashboard', function (): void {
    it('allows admin to access the dashboard', function (): void {
        $admin = User::factory()->admin()->create();

        $this->actingAs($admin)
            ->get($this->adminUrl.'/dashboard')
            ->assertOk()
            ->assertInertia(fn ($page) => $page
                ->component('Admin/Dashboard')
                ->has('totalCustomers')
                ->has('newCustomersThisMonth')
                ->has('mrr')
                ->has('mrrChangePercent')
                ->has('arr')
                ->has('activeServices')
                ->has('serviceBreakdown')
                ->has('totalTransactionRevenue')
                ->has('estimatedFees')
                ->has('netRevenue')
                ->has('revenueThisMonth')
                ->has('overdueCount')
                ->has('overdueAmount')
                ->has('currentChurnRate')
                ->has('churnHealthStatus')
                ->has('revenueByMonth')
                ->missing('recentInvoices')
                ->missing('recentSubscriptions')
                ->missing('popularPlans')
                ->missing('totalRevenue')
                ->missing('customerGrowth')
                ->missing('churnData')
                ->missing('overdueInvoices')
            );
    });

    it('denies customer access to the admin dashboard', function (): void {
        $customer = User::factory()->customer()->create();

        $this->actingAs($customer)
            ->get($this->adminUrl.'/dashboard')
            ->assertForbidden();
    });

    it('redirects guest to login when accessing admin dashboard', function (): void {
        $this->get($this->adminUrl.'/dashboard')
            ->assertRedirect();
    });
});

// ---------------------------------------------------------------------------
// Customer Management
// ---------------------------------------------------------------------------
describe('Customer Management', function (): void {
    it('displays the customer list page', function (): void {
        $admin = User::factory()->admin()->create();
        User::factory()->customer()->count(3)->create();

        $this->actingAs($admin)
            ->get($this->adminUrl.'/customers')
            ->assertOk()
            ->assertInertia(fn ($page) => $page
                ->component('Admin/Customers/Index')
                ->has('customers.data', 3)
                ->has('filters')
            );
    });

    it('filters customers by search query', function (): void {
        $admin = User::factory()->admin()->create();
        User::factory()->customer()->create(['name' => 'Alice Wonderland']);
        User::factory()->customer()->create(['name' => 'Bob Builder']);

        $this->actingAs($admin)
            ->get($this->adminUrl.'/customers?search=Alice')
            ->assertOk()
            ->assertInertia(fn ($page) => $page
                ->component('Admin/Customers/Index')
                ->has('customers.data', 1)
            );
    });

    it('shows a customer detail page with services and invoices', function (): void {
        $admin = User::factory()->admin()->create();
        $customer = User::factory()->customer()->create();
        Service::factory()->create(['user_id' => $customer->id]);
        Invoice::factory()->count(2)->create(['user_id' => $customer->id]);

        $this->actingAs($admin)
            ->get($this->adminUrl.'/customers/'.$customer->id)
            ->assertOk()
            ->assertInertia(fn ($page) => $page
                ->component('Admin/Customers/Show')
                ->has('customer')
                ->has('recentInvoices')
                ->has('auditLogs')
            );
    });

    it('suspends a customer account', function (): void {
        $admin = User::factory()->admin()->create();
        $customer = User::factory()->customer()->create();

        $this->actingAs($admin)
            ->post($this->adminUrl.'/customers/'.$customer->id.'/suspend')
            ->assertRedirect();

        expect($customer->fresh()->status)->toBe('suspended');
        $this->assertDatabaseHas('audit_logs', [
            'user_id' => $customer->id,
            'admin_id' => $admin->id,
            'action' => 'suspend_account',
        ]);
    });

    it('unsuspends a customer account', function (): void {
        $admin = User::factory()->admin()->create();
        $customer = User::factory()->customer()->suspended()->create();

        $this->actingAs($admin)
            ->post($this->adminUrl.'/customers/'.$customer->id.'/unsuspend')
            ->assertRedirect();

        expect($customer->fresh()->status)->toBe('active');
        $this->assertDatabaseHas('audit_logs', [
            'user_id' => $customer->id,
            'admin_id' => $admin->id,
            'action' => 'unsuspend_account',
        ]);
    });
});

// ---------------------------------------------------------------------------
// Plan Management
// ---------------------------------------------------------------------------
describe('Plan Management', function (): void {
    it('displays the plan index page', function (): void {
        $admin = User::factory()->admin()->create();
        Plan::factory()->count(4)->create();

        $this->actingAs($admin)
            ->get($this->adminUrl.'/plans')
            ->assertOk()
            ->assertInertia(fn ($page) => $page
                ->component('Admin/Plans/Index')
                ->has('plans', 4)
                ->has('filters')
            );
    });

    it('filters plans by service type', function (): void {
        $admin = User::factory()->admin()->create();
        Plan::factory()->create(['service_type' => 'vps']);
        Plan::factory()->create(['service_type' => 'dedicated']);
        Plan::factory()->create(['service_type' => 'vps']);

        $this->actingAs($admin)
            ->get($this->adminUrl.'/plans?service_type=vps')
            ->assertOk()
            ->assertInertia(fn ($page) => $page
                ->component('Admin/Plans/Index')
                ->has('plans', 2)
            );
    });

    it('displays the create plan page', function (): void {
        $admin = User::factory()->admin()->create();

        $this->actingAs($admin)
            ->get($this->adminUrl.'/plans/create')
            ->assertOk()
            ->assertInertia(fn ($page) => $page
                ->component('Admin/Plans/Create')
            );
    });

    it('stores a new plan', function (): void {
        $admin = User::factory()->admin()->create();

        $this->actingAs($admin)
            ->post($this->adminUrl.'/plans', [
                'name' => 'Starter VPS',
                'slug' => 'starter-vps',
                'description' => 'A basic VPS plan',
                'service_type' => 'vps',
                'price' => 9.99,
                'billing_cycle' => 'monthly',
                'features' => [
                    ['key' => 'cpu', 'value' => '2 vCPU'],
                    ['key' => 'ram', 'value' => '4GB'],
                ],
                'provisioning_config' => [
                    'package_id' => 1,
                ],
                'stock_quantity' => 100,
                'sort_order' => 1,
            ])
            ->assertRedirect();

        $this->assertDatabaseHas('plans', [
            'name' => 'Starter VPS',
            'slug' => 'starter-vps',
            'service_type' => 'vps',
            'status' => 'active',
        ]);
    });

    it('validates required fields when storing a plan', function (): void {
        $admin = User::factory()->admin()->create();

        $this->actingAs($admin)
            ->post($this->adminUrl.'/plans', [])
            ->assertSessionHasErrors(['name', 'slug', 'service_type', 'price', 'billing_cycle']);
    });

    it('displays the edit plan page', function (): void {
        $admin = User::factory()->admin()->create();
        $plan = Plan::factory()->create();

        $this->actingAs($admin)
            ->get($this->adminUrl.'/plans/'.$plan->id.'/edit')
            ->assertOk()
            ->assertInertia(fn ($page) => $page
                ->component('Admin/Plans/Edit')
                ->has('plan')
                ->has('subscribersCount')
            );
    });

    it('updates an existing plan', function (): void {
        $admin = User::factory()->admin()->create();
        $plan = Plan::factory()->create(['name' => 'Old Name', 'slug' => 'old-name']);

        $this->actingAs($admin)
            ->put($this->adminUrl.'/plans/'.$plan->id, [
                'name' => 'New Name',
                'slug' => 'new-name',
                'service_type' => $plan->service_type,
                'price' => 19.99,
                'billing_cycle' => 'monthly',
                'sort_order' => 0,
            ])
            ->assertRedirect();

        expect($plan->fresh())
            ->name->toBe('New Name')
            ->slug->toBe('new-name');
    });

    it('archives a plan on destroy', function (): void {
        $admin = User::factory()->admin()->create();
        $plan = Plan::factory()->create(['status' => 'active']);

        $this->actingAs($admin)
            ->delete($this->adminUrl.'/plans/'.$plan->id)
            ->assertRedirect();

        expect($plan->fresh()->status)->toBe('inactive');
    });
});

// ---------------------------------------------------------------------------
// Service Management
// ---------------------------------------------------------------------------
describe('Service Management', function (): void {
    it('displays the service list page', function (): void {
        $admin = User::factory()->admin()->create();
        $customer = User::factory()->customer()->create();
        Service::factory()->count(3)->create(['user_id' => $customer->id]);

        $this->actingAs($admin)
            ->get($this->adminUrl.'/services')
            ->assertOk()
            ->assertInertia(fn ($page) => $page
                ->component('Admin/Services/Index')
                ->has('services.data', 3)
                ->has('filters')
            );
    });

    it('shows a service detail page', function (): void {
        $admin = User::factory()->admin()->create();
        $service = Service::factory()->create();

        $this->actingAs($admin)
            ->get($this->adminUrl.'/services/'.$service->id)
            ->assertOk()
            ->assertInertia(fn ($page) => $page
                ->component('Admin/Services/Show')
                ->has('service')
            );
    });

    it('suspends a service', function (): void {
        $admin = User::factory()->admin()->create();
        $service = Service::factory()->create(['status' => 'active']);

        $this->actingAs($admin)
            ->post($this->adminUrl.'/services/'.$service->id.'/suspend')
            ->assertRedirect();

        expect($service->fresh()->status)->toBe('suspended');
        $this->assertDatabaseHas('audit_logs', [
            'user_id' => $service->user_id,
            'admin_id' => $admin->id,
            'action' => 'suspend_service',
            'resource_type' => 'service',
            'resource_id' => $service->id,
        ]);
    });

    it('unsuspends a service', function (): void {
        $admin = User::factory()->admin()->create();
        $service = Service::factory()->suspended()->create();

        $this->actingAs($admin)
            ->post($this->adminUrl.'/services/'.$service->id.'/unsuspend')
            ->assertRedirect();

        expect($service->fresh()->status)->toBe('active');
        $this->assertDatabaseHas('audit_logs', [
            'action' => 'unsuspend_service',
            'resource_id' => $service->id,
        ]);
    });

    it('terminates a service', function (): void {
        $admin = User::factory()->admin()->create();
        $service = Service::factory()->create(['status' => 'active']);

        $this->actingAs($admin)
            ->post($this->adminUrl.'/services/'.$service->id.'/terminate')
            ->assertRedirect();

        $freshService = $service->fresh();
        expect($freshService->status)->toBe('terminated');
        expect($freshService->terminated_at)->not->toBeNull();
        $this->assertDatabaseHas('audit_logs', [
            'action' => 'terminate_service',
            'resource_id' => $service->id,
        ]);
    });
});

// ---------------------------------------------------------------------------
// Invoice Management
// ---------------------------------------------------------------------------
describe('Invoice Management', function (): void {
    it('displays the invoice list page', function (): void {
        $admin = User::factory()->admin()->create();
        $customer = User::factory()->customer()->create();
        Invoice::factory()->count(5)->create(['user_id' => $customer->id]);

        $this->actingAs($admin)
            ->get($this->adminUrl.'/invoices')
            ->assertOk()
            ->assertInertia(fn ($page) => $page
                ->component('Admin/Invoices/Index')
                ->has('invoices.data', 5)
                ->has('filters')
            );
    });

    it('filters invoices by status', function (): void {
        $admin = User::factory()->admin()->create();
        $customer = User::factory()->customer()->create();
        Invoice::factory()->count(2)->create(['user_id' => $customer->id, 'status' => 'paid']);
        Invoice::factory()->count(3)->create(['user_id' => $customer->id, 'status' => 'draft']);

        $this->actingAs($admin)
            ->get($this->adminUrl.'/invoices?status=paid')
            ->assertOk()
            ->assertInertia(fn ($page) => $page
                ->component('Admin/Invoices/Index')
                ->has('invoices.data', 2)
            );
    });

    it('shows an invoice detail page', function (): void {
        $admin = User::factory()->admin()->create();
        $invoice = Invoice::factory()->create();

        $this->actingAs($admin)
            ->get($this->adminUrl.'/invoices/'.$invoice->id)
            ->assertOk()
            ->assertInertia(fn ($page) => $page
                ->component('Admin/Invoices/Show')
                ->has('invoice')
            );
    });

    it('voids an invoice', function (): void {
        $admin = User::factory()->admin()->create();
        $invoice = Invoice::factory()->create(['status' => 'sent']);

        $this->actingAs($admin)
            ->post($this->adminUrl.'/invoices/'.$invoice->id.'/void')
            ->assertRedirect();

        expect($invoice->fresh()->status)->toBe('void');
        $this->assertDatabaseHas('audit_logs', [
            'user_id' => $invoice->user_id,
            'admin_id' => $admin->id,
            'action' => 'void_invoice',
            'resource_type' => 'invoice',
            'resource_id' => $invoice->id,
        ]);
    });
});

// ---------------------------------------------------------------------------
// Coupon Management
// ---------------------------------------------------------------------------
describe('Coupon Management', function (): void {
    it('displays the coupon index page', function (): void {
        $admin = User::factory()->admin()->create();
        Coupon::factory()->count(3)->create();

        $this->actingAs($admin)
            ->get($this->adminUrl.'/coupons')
            ->assertOk()
            ->assertInertia(fn ($page) => $page
                ->component('Admin/Coupons/Index')
                ->has('coupons.data', 3)
            );
    });

    it('displays the coupon show page with redemption history and stats', function (): void {
        $admin = User::factory()->admin()->create();
        $customer = User::factory()->create();
        $coupon = Coupon::factory()->create();

        CouponRedemption::query()->create([
            'coupon_id' => $coupon->id,
            'user_id' => $customer->id,
            'discount_amount' => 15.00,
        ]);

        $this->actingAs($admin)
            ->get($this->adminUrl.'/coupons/'.$coupon->id)
            ->assertOk()
            ->assertInertia(fn ($page) => $page
                ->component('Admin/Coupons/Show')
                ->has('coupon')
                ->has('redemptions.data', 1)
                ->has('stats', fn ($stats) => $stats
                    ->where('total_redemptions', 1)
                    ->where('total_discount', 15)
                    ->has('latest_redemption')
                )
            );
    });

    it('displays the create coupon page', function (): void {
        $admin = User::factory()->admin()->create();

        $this->actingAs($admin)
            ->get($this->adminUrl.'/coupons/create')
            ->assertOk()
            ->assertInertia(fn ($page) => $page
                ->component('Admin/Coupons/Create')
                ->has('plans')
            );
    });

    it('stores a new coupon', function (): void {
        $admin = User::factory()->admin()->create();

        $this->actingAs($admin)
            ->post($this->adminUrl.'/coupons', [
                'code' => 'SAVE20',
                'type' => 'percentage',
                'value' => 20,
                'max_uses' => 100,
                'expires_at' => now()->addMonth()->toDateTimeString(),
            ])
            ->assertRedirect();

        $this->assertDatabaseHas('coupons', [
            'code' => 'SAVE20',
            'type' => 'percentage',
            'active' => true,
        ]);
    });

    it('validates required fields when storing a coupon', function (): void {
        $admin = User::factory()->admin()->create();

        $this->actingAs($admin)
            ->post($this->adminUrl.'/coupons', [])
            ->assertSessionHasErrors(['code', 'type', 'value']);
    });

    it('prevents duplicate coupon codes', function (): void {
        $admin = User::factory()->admin()->create();
        Coupon::factory()->create(['code' => 'EXISTING']);

        $this->actingAs($admin)
            ->post($this->adminUrl.'/coupons', [
                'code' => 'EXISTING',
                'type' => 'percentage',
                'value' => 10,
            ])
            ->assertSessionHasErrors(['code']);
    });

    it('displays the edit coupon page', function (): void {
        $admin = User::factory()->admin()->create();
        $coupon = Coupon::factory()->create();

        $this->actingAs($admin)
            ->get($this->adminUrl.'/coupons/'.$coupon->id.'/edit')
            ->assertOk()
            ->assertInertia(fn ($page) => $page
                ->component('Admin/Coupons/Edit')
                ->has('coupon')
                ->has('plans')
                ->has('redemptions')
            );
    });

    it('updates an existing coupon', function (): void {
        $admin = User::factory()->admin()->create();
        $coupon = Coupon::factory()->create(['code' => 'OLD10', 'value' => 10]);

        $this->actingAs($admin)
            ->put($this->adminUrl.'/coupons/'.$coupon->id, [
                'code' => 'NEW25',
                'type' => 'fixed',
                'value' => 25,
                'max_uses' => 50,
            ])
            ->assertRedirect();

        $fresh = $coupon->fresh();
        expect($fresh->code)->toBe('NEW25');
        expect((float) $fresh->value)->toBe(25.0);
    });

    it('deactivates a coupon on destroy', function (): void {
        $admin = User::factory()->admin()->create();
        $coupon = Coupon::factory()->create(['active' => true]);

        $this->actingAs($admin)
            ->delete($this->adminUrl.'/coupons/'.$coupon->id)
            ->assertRedirect();

        expect($coupon->fresh()->active)->toBeFalsy();
    });
});

// ---------------------------------------------------------------------------
// Audit Logs
// ---------------------------------------------------------------------------
describe('Audit Logs', function (): void {
    it('displays the audit log index page', function (): void {
        $admin = User::factory()->admin()->create();
        AuditLog::factory()->count(5)->create();

        $this->actingAs($admin)
            ->get($this->adminUrl.'/audit-logs')
            ->assertOk()
            ->assertInertia(fn ($page) => $page
                ->component('Admin/AuditLogs/Index')
                ->has('auditLogs.data', 5)
                ->has('actions')
                ->has('filters')
            );
    });

    it('filters audit logs by action', function (): void {
        $admin = User::factory()->admin()->create();
        AuditLog::factory()->count(2)->create(['action' => 'login']);
        AuditLog::factory()->count(3)->create(['action' => 'payment_failed']);

        $this->actingAs($admin)
            ->get($this->adminUrl.'/audit-logs?action=login')
            ->assertOk()
            ->assertInertia(fn ($page) => $page
                ->component('Admin/AuditLogs/Index')
                ->has('auditLogs.data', 2)
            );
    });

    it('denies customer access to audit logs', function (): void {
        $customer = User::factory()->customer()->create();

        $this->actingAs($customer)
            ->get($this->adminUrl.'/audit-logs')
            ->assertForbidden();
    });
});

// ---------------------------------------------------------------------------
// Settings
// ---------------------------------------------------------------------------
describe('Settings', function (): void {
    it('displays the settings index page', function (): void {
        $admin = User::factory()->admin()->create();

        $this->actingAs($admin)
            ->get($this->adminUrl.'/settings')
            ->assertOk()
            ->assertInertia(fn ($page) => $page
                ->component('Admin/Settings/Index')
                ->has('settings')
            );
    });

    it('updates general settings', function (): void {
        $admin = User::factory()->admin()->create();

        $this->actingAs($admin)
            ->put($this->adminUrl.'/settings', [
                'group' => 'general',
                'company_name' => 'EZSCALE Cloud',
                'company_email' => 'hello@ezscale.cloud',
            ])
            ->assertRedirect();

        expect(Setting::get('company_name'))->toBe('EZSCALE Cloud');
        expect(Setting::get('company_email'))->toBe('hello@ezscale.cloud');
    });

    it('updates billing settings', function (): void {
        $admin = User::factory()->admin()->create();

        $this->actingAs($admin)
            ->put($this->adminUrl.'/settings', [
                'group' => 'billing',
                'default_currency' => 'USD',
                'grace_period_days' => 14,
                'suspension_warning_days' => 5,
                'auto_terminate_days' => 30,
                'bandwidth_overage_rate' => 0.10,
            ])
            ->assertRedirect();

        expect(Setting::get('grace_period_days'))->toBe('14');
    });

    it('validates settings update with invalid data', function (): void {
        $admin = User::factory()->admin()->create();

        $this->actingAs($admin)
            ->put($this->adminUrl.'/settings', [
                'group' => 'general',
                'company_email' => 'not-an-email',
            ])
            ->assertSessionHasErrors(['company_email']);
    });

    it('denies customer access to settings', function (): void {
        $customer = User::factory()->customer()->create();

        $this->actingAs($customer)
            ->get($this->adminUrl.'/settings')
            ->assertForbidden();
    });
});
