<?php

namespace App\Classes\Modules\Transactions\Services;

use App\Classes\Modules\Bookings\Services\FetchesBooking;
use App\Classes\Modules\Companies\Services\FetchesCompanyPaymentAttemptLimit;
use App\Classes\Modules\Bookings\Services\CalculatesBookingOutstanding;
use App\Classes\Modules\Bookings\Services\CalculatesBookingPaidAmount;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;

class CalculatesTransactionExpiryDateTime
{
    /** @var FetchesBooking */
    private $fetchesBooking;

    /** @var FetchesCompanyPaymentAttemptLimit */
    private $fetchesCompanyPaymentAttemptLimit;

    /** @var CalculatesBookingOutstanding */
    private $calculatesBookingOutstanding;

    /** @var CalculatesBookingPaidAmount */
    private $calculatesBookingPaidAmount;

    /**
     * CalculatesTransactionExpiryDateTime constructor.
     * @param FetchesBooking $fetchesBooking
     * @param FetchesCompanyPaymentAttemptLimit $fetchesCompanyPaymentAttemptLimit
     * @param CalculatesBookingOutstanding $calculatesBookingOutstanding
     * @param CalculatesBookingPaidAmount $calculatesBookingPaidAmount
     */
    public function __construct(FetchesBooking $fetchesBooking, FetchesCompanyPaymentAttemptLimit $fetchesCompanyPaymentAttemptLimit, CalculatesBookingOutstanding $calculatesBookingOutstanding, CalculatesBookingPaidAmount $calculatesBookingPaidAmount)
    {
        $this->fetchesBooking = $fetchesBooking;
        $this->fetchesCompanyPaymentAttemptLimit = $fetchesCompanyPaymentAttemptLimit;
        $this->calculatesBookingOutstanding = $calculatesBookingOutstanding;
        $this->calculatesBookingPaidAmount = $calculatesBookingPaidAmount;
    }


    /**
     * @param int $bookingId
     * @return Carbon|null $returnDateTime
     */
    public function execute(int $bookingId)
    {
        $booking = $this->fetchesBooking->execute(['id' => $bookingId]);
        $paymentAttemptLimit = $this->fetchesCompanyPaymentAttemptLimit->execute($booking->company);

        $createdAt = Carbon::parse($booking->created_at);
        Log::info("1. Booking created at {$createdAt}.");

        $bookingExpiresAt = $createdAt->addMinutes($paymentAttemptLimit);
        $newBookingExpiresAt = $this->getNewBookingExpiresAt($booking, $bookingExpiresAt, $paymentAttemptLimit);
        $newBookingExpiresAt = $this->getNewBookingExpiresAt2($booking, $paymentAttemptLimit) ?? $newBookingExpiresAt; //Overuled all previous expiry datetime
        $returnDateTime = $newBookingExpiresAt ? $newBookingExpiresAt : $bookingExpiresAt;

        return $returnDateTime;
    }

    private function getNewBookingExpiresAt($booking, $bookingExpiresAt, $paymentAttemptLimit){
        $isExpired = false;
        Log::info("1. Booking expires at {$bookingExpiresAt}. (original)");
        $newBookingExpiresAt = null;
        $now = Carbon::now();

        if ($now->greaterThan($bookingExpiresAt)) {
            $isExpired = true;
        }

        $allPayments = $booking->transactions()
            ->payments()
            ->get();

        // if ($isExpired) {
            $filteredPayments = $allPayments->filter(function ($payment) use ($bookingExpiresAt) {
                return Carbon::parse($payment->created_at)->lessThanOrEqualTo($bookingExpiresAt);
            });

            if ($filteredPayments->isNotEmpty()) {
                // Use the earlier payment to recalculate bookingExpiresAt
                $earliestPayment = $filteredPayments->sortBy('created_at')->first();
                Log::info("2. Booking earliest payment : {$earliestPayment->id}, {$earliestPayment->created_at}");
                $newBookingExpiresAt = Carbon::parse($earliestPayment->created_at)->addMinutes($paymentAttemptLimit);
                Log::info("2. Booking expires at : {$newBookingExpiresAt}. (new)");
                $logDetails = [
                    'booking_id' => $booking->id,
                    'initial_created_at' => $booking->created_at,
                    'original_expiry' => $bookingExpiresAt->toDateTimeString(),
                    'new_expiry' => $newBookingExpiresAt->toDateTimeString(),
                    'valid_payments' => []
                ];
                foreach ($filteredPayments as $payment) {
                    $logDetails['valid_payments'][] = [
                        'payment_id' => $payment->id,
                        'created_at' => $payment->created_at,
                        'amount' => $payment->amount,
                    ];
                }
                Log::info("2. Booking initially expired, but found valid pending payment(s).", $logDetails);

                $isExpired = Carbon::now()->greaterThan($newBookingExpiresAt);
                Log::info("2. Booking expired: {$isExpired}");
            } else {
                Log::info("Booking expired and no valid pending payments for booking ID: {$booking->id}");
            }
        // }

        return $newBookingExpiresAt;
    }


    private function getNewBookingExpiresAt2($booking, $paymentAttemptLimit){
        $newBookingExpiresAt = null;
        $amountOutstanding = $this->calculatesBookingOutstanding->executeWithoutFloatingAmount($booking);
        $amountPaid = $this->calculatesBookingPaidAmount->execute($booking);
        if($amountPaid > 0.01 && $amountOutstanding > 0){
            $newBookingExpiresAt = Carbon::now()->addMinutes($paymentAttemptLimit);
            Log::info("3. Overule booking expired: {$newBookingExpiresAt}");
        }
        return $newBookingExpiresAt;
    }
}
