<?php

namespace App\Repositories\Eloquent;

use App\Models\Booking;
use App\Models\ProfessionalPayout;
use Illuminate\Support\Facades\DB;

class DisbursementManagementRepository
{
    public function stats(): array
    {
        $eligible = Booking::where('payment_status', 'paid')->whereNotNull('consultation_closed_at')->whereNull('professional_payout_id');

        return [
            'eligible_count' => (clone $eligible)->count(),
            'eligible_amount' => (float) (clone $eligible)->sum(DB::raw('COALESCE(professional_net_amount, amount)')),
            'paid_amount' => (float) ProfessionalPayout::where('status', 'paid')->sum('amount'),
            'payout_count' => ProfessionalPayout::where('status', 'paid')->count(),
        ];
    }

    public function eligible(array $filters = [])
    {
        return Booking::with(['client.profile', 'professional.user.profile', 'professional.profession'])
            ->where('payment_status', 'paid')->whereNotNull('consultation_closed_at')->whereNull('professional_payout_id')
            ->when($filters['professional'] ?? null, fn ($query, $name) => $query->whereHas('professional.user.profile', fn ($profile) => $profile->where('full_name', 'like', "%{$name}%")))
            ->when($filters['date_from'] ?? null, fn ($query, $date) => $query->whereDate('date', '>=', $date))
            ->when($filters['date_to'] ?? null, fn ($query, $date) => $query->whereDate('date', '<=', $date))
            ->latest('consultation_closed_at')->paginate(12, ['*'], 'eligible_page')->withQueryString();
    }

    public function history(array $filters = [])
    {
        return ProfessionalPayout::with(['professional.user.profile', 'professional.profession'])->withCount('bookings')
            ->when($filters['professional'] ?? null, fn ($query, $name) => $query->whereHas('professional.user.profile', fn ($profile) => $profile->where('full_name', 'like', "%{$name}%")))
            ->latest('paid_at')->paginate(10, ['*'], 'history_page')->withQueryString();
    }

    public function release(array $bookingIds, array $proofs): array
    {
        return DB::transaction(function () use ($bookingIds, $proofs): array {
            $bookings = Booking::whereIn('id', $bookingIds)->where('payment_status', 'paid')
                ->whereNotNull('consultation_closed_at')->whereNull('professional_payout_id')->lockForUpdate()->get();

            if ($bookings->count() !== count($bookingIds)) {
                throw new \RuntimeException('One or more selected bookings are no longer eligible for disbursement. Refresh the page and try again.');
            }

            $payouts = collect();
            foreach ($bookings->groupBy('professional_id') as $professionalId => $professionalBookings) {
                if (! isset($proofs[$professionalId])) {
                    throw new \RuntimeException('Upload proof of payment for every selected professional.');
                }

                $payout = ProfessionalPayout::create([
                    'professional_id' => $professionalId,
                    'amount' => $professionalBookings->sum(fn (Booking $booking) => $booking->payoutAmount()),
                    'status' => 'paid',
                    'reference' => $this->nextReference(),
                    'proof_path' => $proofs[$professionalId]['path'],
                    'proof_original_name' => $proofs[$professionalId]['original_name'],
                    'proof_mime_type' => $proofs[$professionalId]['mime_type'],
                    'paid_at' => now(),
                ]);
                Booking::whereIn('id', $professionalBookings->pluck('id'))->update([
                    'professional_payout_id' => $payout->id,
                    'released_at' => now(),
                    'updated_at' => now(),
                ]);
                $payouts->push($payout);
            }

            return [
                'payouts' => $payouts->count(),
                'bookings' => $bookings->count(),
                'amount' => (float) $bookings->sum(fn (Booking $booking) => $booking->payoutAmount()),
                'records' => ProfessionalPayout::with(['professional.user.profile', 'professional.profession', 'bookings.client.profile'])
                    ->whereIn('id', $payouts->pluck('id'))->get(),
            ];
        });
    }

    private function nextReference(): string
    {
        do {
            $reference = 'DSX-'.now()->format('Ymd').'-'.strtoupper(str()->random(8));
        } while (ProfessionalPayout::where('reference', $reference)->exists());

        return $reference;
    }
}
