<?php

namespace App\Services;

use App\Models\Payment;
use App\Models\SubscriptionPlan;
use App\Models\UserSubscription;
use Carbon\Carbon;
use Carbon\CarbonPeriod;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;

class RevenueAnalyticsService
{
    public function dashboardData(Request $request): array
    {
        [$startDate, $endDate, $filterLabel] = $this->resolveRange($request);

        return [
            'range' => [
                'start' => $startDate,
                'end' => $endDate,
                'label' => $filterLabel,
                'filter' => $request->query('filter', 'month'),
            ],
            'revenue' => [
                'filtered' => $this->successfulPaymentsBetween($startDate, $endDate)->sum('amount'),
                'total' => $this->successfulPayments()->sum('amount'),
                'today' => $this->successfulPaymentsBetween(now()->startOfDay(), now()->endOfDay())->sum('amount'),
                'month' => $this->successfulPaymentsBetween(now()->startOfMonth(), now()->endOfMonth())->sum('amount'),
                'year' => $this->successfulPaymentsBetween(now()->startOfYear(), now()->endOfYear())->sum('amount'),
            ],
            'subscriptions' => [
                'active' => UserSubscription::where('status', UserSubscription::STATUS_ACTIVE)->count(),
                'expired' => UserSubscription::where('status', UserSubscription::STATUS_EXPIRED)->count(),
                'trial' => UserSubscription::where('amount_paid', 0)->where('status', UserSubscription::STATUS_ACTIVE)->count(),
                'cancelled' => UserSubscription::where('status', UserSubscription::STATUS_CANCELLED)->count(),
            ],
            'payments' => [
                'successful' => Payment::where('status', Payment::STATUS_SUCCESSFUL)->count(),
                'failed' => Payment::where('status', Payment::STATUS_FAILED)->count(),
                'pending' => Payment::where('status', Payment::STATUS_PENDING)->count(),
            ],
            'mostPopularPlan' => $this->mostPopularPlan(),
            'latestSubscribers' => UserSubscription::with(['user', 'plan'])->latest()->take(5)->get(),
            'latestPayments' => Payment::with(['user', 'plan', 'gateway'])->latest()->take(5)->get(),
            'charts' => [
                'monthlyRevenue' => $this->monthlyRevenueChart(),
                'subscriberGrowth' => $this->subscriberGrowthChart(),
                'gatewayRevenue' => $this->gatewayRevenueChart(),
                'planPopularity' => $this->planPopularityChart(),
            ],
        ];
    }

    private function resolveRange(Request $request): array
    {
        $filter = $request->query('filter', 'month');

        if ($filter === 'today') {
            return [now()->startOfDay(), now()->endOfDay(), 'Today'];
        }

        if ($filter === 'year') {
            return [now()->startOfYear(), now()->endOfYear(), 'This Year'];
        }

        if ($filter === 'custom') {
            $start = Carbon::parse($request->query('start_date', now()->startOfMonth()->toDateString()))->startOfDay();
            $end = Carbon::parse($request->query('end_date', now()->endOfMonth()->toDateString()))->endOfDay();

            if ($end->lessThan($start)) {
                [$start, $end] = [$end->copy()->startOfDay(), $start->copy()->endOfDay()];
            }

            return [$start, $end, 'Custom Range'];
        }

        return [now()->startOfMonth(), now()->endOfMonth(), 'This Month'];
    }

    private function successfulPayments(): Builder
    {
        return Payment::query()->where('status', Payment::STATUS_SUCCESSFUL);
    }

    private function successfulPaymentsBetween(Carbon $startDate, Carbon $endDate): Builder
    {
        return $this->successfulPayments()
            ->where(function (Builder $query) use ($startDate, $endDate): void {
                $query
                    ->whereBetween('paid_at', [$startDate, $endDate])
                    ->orWhere(function (Builder $query) use ($startDate, $endDate): void {
                        $query->whereNull('paid_at')
                            ->whereBetween('created_at', [$startDate, $endDate]);
                    });
            });
    }

    private function mostPopularPlan(): ?SubscriptionPlan
    {
        return SubscriptionPlan::withCount('subscriptions')
            ->orderByDesc('subscriptions_count')
            ->first();
    }

    private function monthlyRevenueChart(): array
    {
        $months = collect(CarbonPeriod::create(now()->startOfYear(), '1 month', now()->endOfYear()))
            ->map(fn (Carbon $date): string => $date->format('Y-m'));

        $payments = $this->successfulPaymentsBetween(now()->startOfYear(), now()->endOfYear())->get();

        return $this->monthChart($months, $payments, fn (Payment $payment): float => (float) $payment->amount);
    }

    private function subscriberGrowthChart(): array
    {
        $months = collect(CarbonPeriod::create(now()->startOfYear(), '1 month', now()->endOfYear()))
            ->map(fn (Carbon $date): string => $date->format('Y-m'));

        $subscriptions = UserSubscription::whereBetween('created_at', [now()->startOfYear(), now()->endOfYear()])->get();

        return $this->monthChart($months, $subscriptions, fn (): int => 1);
    }

    private function gatewayRevenueChart(): array
    {
        $payments = $this->successfulPayments()
            ->with('gateway')
            ->get()
            ->groupBy(fn (Payment $payment): string => $payment->gateway?->name ?? 'No Gateway');

        return [
            'labels' => $payments->keys()->values(),
            'data' => $payments->map(fn (Collection $group): float => round((float) $group->sum('amount'), 2))->values(),
        ];
    }

    private function planPopularityChart(): array
    {
        $plans = SubscriptionPlan::withCount('subscriptions')
            ->orderByDesc('subscriptions_count')
            ->get();

        return [
            'labels' => $plans->pluck('name')->values(),
            'data' => $plans->pluck('subscriptions_count')->values(),
        ];
    }

    private function monthChart(Collection $months, Collection $records, callable $valueResolver): array
    {
        $values = $months->map(function (string $month) use ($records, $valueResolver): float {
            return round((float) $records
                ->filter(fn ($record): bool => $record->created_at->format('Y-m') === $month)
                ->sum($valueResolver), 2);
        });

        return [
            'labels' => $months->map(fn (string $month): string => Carbon::createFromFormat('Y-m', $month)->format('M'))->values(),
            'data' => $values->values(),
        ];
    }
}
