<?php

namespace App\Repositories\Eloquent;

use App\Models\Booking;
use App\Models\Professional;
use App\Models\Rating;
use App\Models\User;
use App\Repositories\Contracts\DashboardRepositoryInterface;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;

class DashboardRepository implements DashboardRepositoryInterface
{
    public function dashboard(array $filters = []): array
    {
        $trendPeriod = $filters['trend_period'] ?? 'month';
        $anchor = Carbon::parse($filters['trend_anchor'] ?? now());
        $period = $this->dashboardTrendPeriod($trendPeriod, $anchor);
        $periodBookings = Booking::whereBetween('created_at', [$period['start'], $period['end']])
            ->get(['created_at', 'amount', 'payment_status', 'platform_fee_amount', 'convenience_fee_amount'])
            ->groupBy(fn ($booking) => $booking->created_at->format($period['key_format']));
        $periodUsers = User::whereBetween('created_at', [$period['start'], $period['end']])->get(['created_at'])
            ->groupBy(fn ($user) => $user->created_at->format($period['key_format']))->map->count();
        $paidBookings = Booking::where('payment_status', 'paid');
        $eligiblePayouts = Booking::where('payment_status', 'paid')
            ->whereNotNull('consultation_closed_at')->whereNull('professional_payout_id');

        $bookingStatuses = [
            'Pending' => Booking::where('status', 'pending')->count(),
            'Accepted' => Booking::where('status', 'accepted')->whereNull('consultation_closed_at')->count(),
            'Completed' => Booking::whereNotNull('consultation_closed_at')->count(),
            'Declined' => Booking::where('status', 'declined')->count(),
            'Cancelled' => Booking::where('status', 'cancelled')->count(),
            'Expired' => Booking::where('status', 'expired')->count(),
        ];
        $paymentStatuses = collect(['paid', 'pending', 'failed'])->mapWithKeys(
            fn ($status) => [ucfirst($status) => Booking::where('payment_status', $status)->count()]
        );

        return [
            'kpis' => [
                'gross_paid' => (float) (clone $paidBookings)->sum('amount'),
                'platform_revenue' => (float) (clone $paidBookings)->sum(DB::raw('COALESCE(platform_fee_amount,0) + COALESCE(convenience_fee_amount,0)')),
                'unreleased_amount' => (float) (clone $eligiblePayouts)->sum(DB::raw('COALESCE(professional_net_amount,amount)')),
                'unreleased_count' => (clone $eligiblePayouts)->count(),
                'pending_applications' => Professional::where('has_applied', true)->where('status', 'pending')->count(),
                'active_consultations' => Booking::whereNotNull('consultation_started_at')->whereNull('consultation_closed_at')->count(),
                'paid_booking_count' => (clone $paidBookings)->count(),
                'fee_snapshot_count' => (clone $paidBookings)->whereNotNull('platform_fee_amount')->count(),
            ],
            'trend' => $period['buckets']->map(function ($bucket) use ($periodBookings, $periodUsers) {
                $bookings = $periodBookings->get($bucket['key'], collect());

                return (object) [
                    'label' => $bucket['label'],
                    'bookings' => $bookings->count(),
                    'paid_revenue' => (float) $bookings->where('payment_status', 'paid')->sum('amount'),
                    'platform_revenue' => (float) $bookings->where('payment_status', 'paid')->sum(fn ($booking) => (float) ($booking->platform_fee_amount ?? 0) + (float) ($booking->convenience_fee_amount ?? 0)),
                    'users' => $periodUsers->get($bucket['key'], 0),
                ];
            }),
            'trend_period' => $trendPeriod,
            'trend_range' => $period['label'],
            'trend_anchor' => $period['anchor']->format('Y-m-d'),
            'trend_previous_anchor' => $period['previous']->format('Y-m-d'),
            'trend_next_anchor' => $period['next']?->format('Y-m-d'),
            'trend_totals' => [
                'bookings' => $periodBookings->flatten(1)->count(),
                'paid_revenue' => (float) $periodBookings->flatten(1)->where('payment_status', 'paid')->sum('amount'),
                'platform_revenue' => (float) $periodBookings->flatten(1)->where('payment_status', 'paid')->sum(fn ($booking) => (float) ($booking->platform_fee_amount ?? 0) + (float) ($booking->convenience_fee_amount ?? 0)),
            ],
            'booking_statuses' => collect($bookingStatuses),
            'payment_statuses' => $paymentStatuses,
            'booking_types' => collect([
                'Scheduled' => Booking::where('type', 'appointment')->count(),
                'On-demand' => Booking::where('type', 'realtime')->count(),
            ]),
            'top_professions' => Booking::query()
                ->join('professions', 'professions.id', '=', 'bookings.profession_id')
                ->select('professions.name', 'professions.color_hex', DB::raw('COUNT(bookings.id) as bookings_count'), DB::raw("SUM(CASE WHEN bookings.payment_status = 'paid' THEN bookings.amount ELSE 0 END) as paid_value"))
                ->groupBy('professions.id', 'professions.name', 'professions.color_hex')
                ->orderByDesc('bookings_count')->limit(5)->get(),
            'alerts' => [
                ['label' => 'Applications awaiting review', 'value' => Professional::where('has_applied', true)->where('status', 'pending')->count(), 'route' => route('applications', ['status' => 'pending']), 'icon' => 'file-clock'],
                ['label' => 'Unverified user accounts', 'value' => User::where('verified', false)->count(), 'route' => route('users'), 'icon' => 'user-round-x'],
                ['label' => 'Completed payouts ready', 'value' => (clone $eligiblePayouts)->count(), 'route' => route('disbursements'), 'icon' => 'hand-coins'],
                ['label' => 'Accepted bookings awaiting payment', 'value' => Booking::where('status', 'accepted')->where('payment_status', 'pending')->count(), 'route' => route('bookings', ['status' => 'accepted', 'payment_status' => 'pending']), 'icon' => 'credit-card'],
            ],
            'users' => [
                'total' => User::count(),
                'clients' => User::whereHas('role', fn ($query) => $query->where('name', 'Client'))->count(),
                'professionals' => Professional::where('status', 'approved')->count(),
                'new_30d' => User::where('created_at', '>=', now()->subDays(30))->count(),
            ],
            'rating' => (float) (Rating::avg('rate') ?? 0),
            'recent' => Booking::with(['client.profile', 'professional.user.profile', 'professional.profession'])->latest()->limit(6)->get(),
            'generated_at' => now(),
        ];
    }

    private function dashboardTrendPeriod(string $period, Carbon $anchor): array
    {
        if ($period === 'day') {
            $start = $anchor->copy()->startOfDay();
            $end = $anchor->copy()->endOfDay();
            $buckets = collect(range(0, 23))->map(fn ($hour) => [
                'key' => $start->copy()->addHours($hour)->format('Y-m-d H'),
                'label' => $start->copy()->addHours($hour)->format('ga'),
            ]);
            $previous = $anchor->copy()->subDay();
            $next = $anchor->copy()->addDay();
            $label = $start->format('F d, Y');
            $keyFormat = 'Y-m-d H';
        } elseif ($period === 'week') {
            $start = $anchor->copy()->startOfWeek();
            $end = $anchor->copy()->endOfWeek();
            $buckets = collect(range(0, 6))->map(fn ($day) => [
                'key' => $start->copy()->addDays($day)->format('Y-m-d'),
                'label' => $start->copy()->addDays($day)->format('D'),
            ]);
            $previous = $anchor->copy()->subWeek();
            $next = $anchor->copy()->addWeek();
            $label = $start->format('M d').' – '.$end->format('M d, Y');
            $keyFormat = 'Y-m-d';
        } elseif ($period === 'year') {
            $start = $anchor->copy()->startOfYear();
            $end = $anchor->copy()->endOfYear();
            $buckets = collect(range(0, 11))->map(fn ($month) => [
                'key' => $start->copy()->addMonths($month)->format('Y-m'),
                'label' => $start->copy()->addMonths($month)->format('M'),
            ]);
            $previous = $anchor->copy()->subYear();
            $next = $anchor->copy()->addYear();
            $label = $start->format('Y');
            $keyFormat = 'Y-m';
        } else {
            $start = $anchor->copy()->startOfMonth();
            $end = $anchor->copy()->endOfMonth();
            $buckets = collect(range(0, $start->daysInMonth - 1))->map(fn ($day) => [
                'key' => $start->copy()->addDays($day)->format('Y-m-d'),
                'label' => $start->copy()->addDays($day)->format('j'),
            ]);
            $previous = $anchor->copy()->subMonthNoOverflow();
            $next = $anchor->copy()->addMonthNoOverflow();
            $label = $start->format('F Y');
            $keyFormat = 'Y-m-d';
        }

        $next = $next->copy()->startOfDay()->isAfter(now()->startOfDay()) ? null : $next;

        return compact('start', 'end', 'buckets', 'previous', 'next', 'label', 'keyFormat') + [
            'anchor' => $anchor,
            'key_format' => $keyFormat,
        ];
    }
}
