<?php

declare(strict_types=1);

use App\Models\Invoice;
use App\Models\PaymentTransaction;
use App\Models\Plan;
use App\Models\PlanPrice;
use App\Models\Service;
use App\Models\User;
use Database\Seeders\RoleAndPermissionSeeder;
use Illuminate\Support\Facades\Cache;
use Laravel\Cashier\Subscription;

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

describe('Customer count and delta', function (): void {
    it('returns correct totalCustomers and newCustomersThisMonth', function (): void {
        // Customer from last month
        User::factory()->customer()->create([
            'created_at' => now()->subMonth(),
        ]);
        // Two customers this month
        User::factory()->customer()->count(2)->create([
            'created_at' => now(),
        ]);

        $this->actingAs($this->admin)
            ->get($this->adminUrl.'/dashboard')
            ->assertOk()
            ->assertInertia(fn ($page) => $page
                ->where('totalCustomers', 3)
                ->where('newCustomersThisMonth', 2)
            );
    });
});

describe('MRR normalization', function (): void {
    it('normalizes MRR across billing cycles', function (): void {
        $user = User::factory()->customer()->create();

        // Monthly plan at $30/mo → contributes $30 MRR
        $monthlyPlan = Plan::factory()->create(['billing_cycle' => 'monthly', 'price' => 30]);
        PlanPrice::create([
            'plan_id' => $monthlyPlan->id,
            'billing_cycle' => 'monthly',
            'price' => 30.00,
        ]);
        Subscription::create([
            'user_id' => $user->id,
            'type' => 'default',
            'stripe_id' => 'sub_monthly_'.uniqid(),
            'stripe_status' => 'active',
            'stripe_price' => 'price_monthly',
            'plan_id' => $monthlyPlan->id,
            'billing_cycle' => 'monthly',
        ]);

        // Quarterly plan at $90/quarter → contributes $30 MRR
        $quarterlyPlan = Plan::factory()->create(['billing_cycle' => 'quarterly', 'price' => 90]);
        PlanPrice::create([
            'plan_id' => $quarterlyPlan->id,
            'billing_cycle' => 'quarterly',
            'price' => 90.00,
        ]);
        Subscription::create([
            'user_id' => $user->id,
            'type' => 'default',
            'stripe_id' => 'sub_quarterly_'.uniqid(),
            'stripe_status' => 'active',
            'stripe_price' => 'price_quarterly',
            'plan_id' => $quarterlyPlan->id,
            'billing_cycle' => 'quarterly',
        ]);

        // Annual plan at $120/year → contributes $10 MRR
        $annualPlan = Plan::factory()->create(['billing_cycle' => 'annual', 'price' => 120]);
        PlanPrice::create([
            'plan_id' => $annualPlan->id,
            'billing_cycle' => 'annual',
            'price' => 120.00,
        ]);
        Subscription::create([
            'user_id' => $user->id,
            'type' => 'default',
            'stripe_id' => 'sub_annual_'.uniqid(),
            'stripe_status' => 'active',
            'stripe_price' => 'price_annual',
            'plan_id' => $annualPlan->id,
            'billing_cycle' => 'annual',
        ]);

        // Expected MRR: $30 + $30 + $10 = $70
        $this->actingAs($this->admin)
            ->get($this->adminUrl.'/dashboard')
            ->assertOk()
            ->assertInertia(fn ($page) => $page
                ->where('mrr', fn ($mrr) => (float) $mrr === 70.0)
                ->where('arr', fn ($arr) => (float) $arr === 840.0)
            );
    });
});

describe('MRR month-over-month change', function (): void {
    it('calculates mrrChangePercent when previous month data exists', function (): void {
        $user = User::factory()->customer()->create();

        // Subscription created before last month (will count for both current and previous MRR)
        $plan = Plan::factory()->create(['billing_cycle' => 'monthly', 'price' => 50]);
        PlanPrice::create([
            'plan_id' => $plan->id,
            'billing_cycle' => 'monthly',
            'price' => 50.00,
        ]);
        Subscription::create([
            'user_id' => $user->id,
            'type' => 'default',
            'stripe_id' => 'sub_old_'.uniqid(),
            'stripe_status' => 'active',
            'stripe_price' => 'price_old',
            'plan_id' => $plan->id,
            'billing_cycle' => 'monthly',
            'created_at' => now()->subMonths(2),
        ]);

        // New subscription this month (only counts for current MRR)
        $plan2 = Plan::factory()->create(['billing_cycle' => 'monthly', 'price' => 50]);
        PlanPrice::create([
            'plan_id' => $plan2->id,
            'billing_cycle' => 'monthly',
            'price' => 50.00,
        ]);
        Subscription::create([
            'user_id' => $user->id,
            'type' => 'default',
            'stripe_id' => 'sub_new_'.uniqid(),
            'stripe_status' => 'active',
            'stripe_price' => 'price_new',
            'plan_id' => $plan2->id,
            'billing_cycle' => 'monthly',
            'created_at' => now(),
        ]);

        // Current MRR: $100, Previous MRR: $50 → change: +100%
        $this->actingAs($this->admin)
            ->get($this->adminUrl.'/dashboard')
            ->assertOk()
            ->assertInertia(fn ($page) => $page
                ->where('mrr', fn ($mrr) => (float) $mrr === 100.0)
                ->where('mrrChangePercent', fn ($v) => (float) $v === 100.0)
            );
    });

    it('returns null mrrChangePercent when no previous month data', function (): void {
        $user = User::factory()->customer()->create();

        // Subscription created this month only
        $plan = Plan::factory()->create(['billing_cycle' => 'monthly', 'price' => 50]);
        PlanPrice::create([
            'plan_id' => $plan->id,
            'billing_cycle' => 'monthly',
            'price' => 50.00,
        ]);
        Subscription::create([
            'user_id' => $user->id,
            'type' => 'default',
            'stripe_id' => 'sub_new_'.uniqid(),
            'stripe_status' => 'active',
            'stripe_price' => 'price_new',
            'plan_id' => $plan->id,
            'billing_cycle' => 'monthly',
            'created_at' => now(),
        ]);

        $this->actingAs($this->admin)
            ->get($this->adminUrl.'/dashboard')
            ->assertOk()
            ->assertInertia(fn ($page) => $page
                ->where('mrrChangePercent', null)
            );
    });
});

describe('Fee estimation by gateway', function (): void {
    it('estimates fees correctly for Stripe and PayPal transactions', function (): void {
        $user = User::factory()->customer()->create();

        // Stripe: $100 → fee = ($100 * 0.029) + $0.30 = $3.20
        PaymentTransaction::create([
            'user_id' => $user->id,
            'gateway' => 'stripe',
            'gateway_transaction_id' => 'txn_stripe_'.uniqid(),
            'amount' => 100.00,
            'currency' => 'USD',
            'status' => 'succeeded',
            'payment_method' => 'card',
            'description' => 'Test stripe payment',
        ]);

        // PayPal: $200 → fee = ($200 * 0.0349) + $0.49 = $7.47
        PaymentTransaction::create([
            'user_id' => $user->id,
            'gateway' => 'paypal',
            'gateway_transaction_id' => 'txn_paypal_'.uniqid(),
            'amount' => 200.00,
            'currency' => 'USD',
            'status' => 'succeeded',
            'payment_method' => 'paypal',
            'description' => 'Test paypal payment',
        ]);

        // Total fees: $3.20 + $7.47 = $10.67
        $this->actingAs($this->admin)
            ->get($this->adminUrl.'/dashboard')
            ->assertOk()
            ->assertInertia(fn ($page) => $page
                ->where('totalTransactionRevenue', fn ($v) => (float) $v === 300.0)
                ->where('estimatedFees', 10.67)
            );
    });
});

describe('Net revenue', function (): void {
    it('calculates net revenue as total minus fees', function (): void {
        $user = User::factory()->customer()->create();

        PaymentTransaction::create([
            'user_id' => $user->id,
            'gateway' => 'stripe',
            'gateway_transaction_id' => 'txn_'.uniqid(),
            'amount' => 100.00,
            'currency' => 'USD',
            'status' => 'succeeded',
            'payment_method' => 'card',
            'description' => 'Payment',
        ]);

        // Fee: ($100 * 0.029) + $0.30 = $3.20
        // Net: $100 - $3.20 = $96.80
        $this->actingAs($this->admin)
            ->get($this->adminUrl.'/dashboard')
            ->assertOk()
            ->assertInertia(fn ($page) => $page
                ->where('netRevenue', fn ($v) => (float) $v === 96.8)
            );
    });
});

describe('Service breakdown', function (): void {
    it('returns correct service counts by type', function (): void {
        $customer = User::factory()->customer()->create();

        Service::factory()->count(3)->create([
            'user_id' => $customer->id,
            'service_type' => 'vps',
            'status' => 'active',
        ]);
        Service::factory()->count(2)->create([
            'user_id' => $customer->id,
            'service_type' => 'dedicated',
            'status' => 'active',
        ]);
        // Suspended service should not appear
        Service::factory()->create([
            'user_id' => $customer->id,
            'service_type' => 'vps',
            'status' => 'suspended',
        ]);

        $this->actingAs($this->admin)
            ->get($this->adminUrl.'/dashboard')
            ->assertOk()
            ->assertInertia(fn ($page) => $page
                ->where('activeServices', 5)
                ->where('serviceBreakdown.vps', 3)
                ->where('serviceBreakdown.dedicated', 2)
            );
    });
});

describe('Overdue tracking', function (): void {
    it('returns correct overdue count and amount', function (): void {
        $customer = User::factory()->customer()->create();

        Invoice::factory()->count(2)->create([
            'user_id' => $customer->id,
            'status' => 'overdue',
            'total' => 50.00,
        ]);
        // Paid invoice should not count
        Invoice::factory()->create([
            'user_id' => $customer->id,
            'status' => 'paid',
            'total' => 100.00,
        ]);

        $this->actingAs($this->admin)
            ->get($this->adminUrl.'/dashboard')
            ->assertOk()
            ->assertInertia(fn ($page) => $page
                ->where('overdueCount', 2)
                ->where('overdueAmount', fn ($v) => (float) $v === 100.0)
            );
    });
});

describe('Churn health status', function (): void {
    it('returns healthy when churn rate is below 3%', function (): void {
        // No subscriptions → 0% churn → healthy
        $this->actingAs($this->admin)
            ->get($this->adminUrl.'/dashboard')
            ->assertOk()
            ->assertInertia(fn ($page) => $page
                ->where('currentChurnRate', 0)
                ->where('churnHealthStatus', 'healthy')
            );
    });

    it('returns watch when churn rate is between 3% and 7%', function (): void {
        $user = User::factory()->customer()->create();

        // Create 20 subscriptions before this month
        for ($i = 0; $i < 20; $i++) {
            Subscription::create([
                'user_id' => $user->id,
                'type' => 'default',
                'stripe_id' => 'sub_base_'.$i.'_'.uniqid(),
                'stripe_status' => 'active',
                'stripe_price' => 'price_base',
                'created_at' => now()->subMonths(2),
            ]);
        }

        // Cancel 1 this month → 5% churn → watch
        Subscription::create([
            'user_id' => $user->id,
            'type' => 'default',
            'stripe_id' => 'sub_cancelled_'.uniqid(),
            'stripe_status' => 'canceled',
            'stripe_price' => 'price_cancelled',
            'created_at' => now()->subMonths(2),
            'cancelled_at' => now(),
        ]);

        $this->actingAs($this->admin)
            ->get($this->adminUrl.'/dashboard')
            ->assertOk()
            ->assertInertia(fn ($page) => $page
                ->where('churnHealthStatus', 'watch')
            );
    });

    it('returns high when churn rate exceeds 7%', function (): void {
        $user = User::factory()->customer()->create();

        // Create 10 subscriptions before this month
        for ($i = 0; $i < 10; $i++) {
            Subscription::create([
                'user_id' => $user->id,
                'type' => 'default',
                'stripe_id' => 'sub_base_'.$i.'_'.uniqid(),
                'stripe_status' => 'active',
                'stripe_price' => 'price_base',
                'created_at' => now()->subMonths(2),
            ]);
        }

        // Cancel 1 this month → ~9.1% churn (1/11) → high
        Subscription::create([
            'user_id' => $user->id,
            'type' => 'default',
            'stripe_id' => 'sub_cancelled_'.uniqid(),
            'stripe_status' => 'canceled',
            'stripe_price' => 'price_cancelled',
            'created_at' => now()->subMonths(2),
            'cancelled_at' => now(),
        ]);

        $this->actingAs($this->admin)
            ->get($this->adminUrl.'/dashboard')
            ->assertOk()
            ->assertInertia(fn ($page) => $page
                ->where('churnHealthStatus', 'high')
            );
    });
});
