<?php

namespace App\Services;

use App\Models\Package;
use App\Models\Subscription;
use Carbon\Carbon;

class BillingService
{
    /**
     * Calculate billing amount for a package with driver count
     */
    public function calculateBillingAmount(Package $package, int $driverCount): float
    {
        $basePrice = $package->base_price ?? 0;
        $pricePerDriver = $package->price_per_driver ?? 0;

        // Formula: base_price + (driver_count * price_per_driver)
        $totalAmount = $basePrice + ($driverCount * $pricePerDriver);

        return max($totalAmount, 0); // Ensure non-negative
    }

    /**
     * Calculate prorated amount for mid-cycle driver count change
     */
    public function calculateProratedAmount(
        Subscription $subscription,
        int $oldDriverCount,
        int $newDriverCount
    ): float {
        $package = $subscription->package;
        $pricePerDriver = $package->price_per_driver ?? 0;
        $driverDifference = $newDriverCount - $oldDriverCount;

        $billingCycle = $subscription->billing_cycle ?? 'monthly';
        $daysInCycle = $billingCycle === 'weekly' ? 7 : 30;

        // Calculate days remaining in current billing cycle
        $nextBillingDate = $subscription->next_billing_date ?? now()->addMonth();
        $daysRemaining = max(1, now()->diffInDays($nextBillingDate));

        // Proration = daily_rate * drivers_changed * days_remaining
        $dailyRate = ($driverDifference * $pricePerDriver) / $daysInCycle;
        $proratedAmount = $dailyRate * $daysRemaining;

        return $proratedAmount;
    }

    /**
     * Get next billing date based on billing cycle
     */
    public function getNextBillingDate(string $billingCycle): Carbon
    {
        if ($billingCycle === 'weekly') {
            return now()->addWeek();
        }

        return now()->addMonth();
    }

    /**
     * Check if subscription is due for billing
     */
    public function isSubscriptionDueForBilling(Subscription $subscription): bool
    {
        if (!$subscription->next_billing_date) {
            return true;
        }

        return now()->gte($subscription->next_billing_date);
    }

    /**
     * Get all subscriptions due for billing
     */
    public function getSubscriptionsDueForBilling()
    {
        return Subscription::where('status', 'active')
            ->where('payment_status', '!=', 'failed')
            ->where(function ($query) {
                $query->whereNull('next_billing_date')
                    ->orWhere('next_billing_date', '<=', now());
            })
            ->with(['user', 'package', 'user.company'])
            ->get();
    }

    /**
     * Process billing for a subscription
     */
    public function processBilling(Subscription $subscription): void
    {
        try {
            $stripeService = app(StripeService::class);

            // Create payment record
            $payment = $stripeService->chargeSubscription($subscription);

            // Update subscription's next billing date
            $billingCycle = $subscription->billing_cycle ?? $subscription->package->billing_cycle;
            $nextDate = $this->getNextBillingDate($billingCycle);

            $subscription->update([
                'next_billing_date' => $nextDate,
            ]);

        } catch (\Exception $e) {
            \Log::error("Billing failed for subscription {$subscription->id}: {$e->getMessage()}");

            // Update subscription status to failed
            $subscription->update([
                'payment_status' => 'failed',
            ]);
        }
    }

    /**
     * Handle driver count change and apply proration if needed
     */
    public function handleDriverCountChange(Subscription $subscription): void
    {
        $company = $subscription->user->company;
        $currentDriverCount = $company->drivers()->count();
        $lastDriverCount = $subscription->last_driver_count ?? 0;

        if ($currentDriverCount === $lastDriverCount) {
            return;
        }

        // Calculate prorated adjustment
        $proratedAmount = $this->calculateProratedAmount(
            $subscription,
            $lastDriverCount,
            $currentDriverCount
        );

        // Update Stripe subscription for next billing
        $stripeService = app(StripeService::class);
        $stripeService->updateSubscriptionForDriverChange($subscription);

        // If significant change, record adjustment
        if (abs($proratedAmount) > 0.01) {
            // Could create a credit/debit adjustment payment here
            \Log::info("Prorated amount for subscription {$subscription->id}: ${$proratedAmount}");
        }
    }

    /**
     * Generate invoice summary for a subscription
     */
    public function generateInvoiceSummary(Subscription $subscription): array
    {
        $company = $subscription->user->company;
        $package = $subscription->package;
        $driverCount = $company->drivers()->count();

        $basePrice = $package->base_price ?? 0;
        $pricePerDriver = $package->price_per_driver ?? 0;
        $totalAmount = $this->calculateBillingAmount($package, $driverCount);

        return [
            'package_name' => $package->name,
            'billing_cycle' => $subscription->billing_cycle ?? 'monthly',
            'next_billing_date' => $subscription->next_billing_date,
            'base_price' => $basePrice,
            'driver_count' => $driverCount,
            'price_per_driver' => $pricePerDriver,
            'driver_charges' => $driverCount * $pricePerDriver,
            'total_amount' => $totalAmount,
        ];
    }
}
