<?php

namespace App\Services;

use App\Models\Booking;
use App\Models\Payment;
use App\Models\PaymentGateway;
use App\Models\WebhookEvent;
use App\Services\Calendar\GoogleCalendarService;
use App\Services\Payments\PaymentGatewayManager;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;

class PaymentService
{
    public function __construct(
        private PaymentGatewayManager $gateways,
        private EscrowService $escrow,
        private BookingStateService $state,
        private ?GoogleCalendarService $calendar = null,
    ) {
    }

    public function initiate(Booking $booking, PaymentGateway $gateway): Payment
    {
        if (! $gateway->enabled) {
            throw ValidationException::withMessages(['gateway' => 'Gateway disabled']);
        }

        if ($booking->status !== 'pending_payment') {
            $this->state->transition($booking, 'pending_payment');
        }

        $booking->update([
            'payment_gateway_id' => $gateway->id,
            'escrow_amount' => $booking->amount,
        ]);

        $driver = $this->gateways->fromModel($gateway);
        $payment = $driver->createPaymentIntent($booking);

        $this->escrow->recordDeposit($payment);

        if ($booking->booking_mode === 'approval_required') {
            $this->state->transition($booking, 'pending_provider_approval');
        }

        return $payment;
    }

    public function handleWebhook(string $gatewayName, Request $request): ?Payment
    {
        $gateway = PaymentGateway::where('name', $gatewayName)->firstOrFail();
        $driver = $this->gateways->fromModel($gateway);

        $payment = $driver->handleWebhook($request, $gateway);

        WebhookEvent::create([
            'payment_gateway_id' => $gateway->id,
            'event_id' => $payment?->transaction_id,
            'type' => 'webhook',
            'payload' => json_decode($request->getContent(), true),
            'processed' => true,
            'processed_at' => now(),
        ]);

        if ($payment && $payment->status === 'captured') {
            DB::transaction(function () use ($payment) {
                $this->escrow->hold($payment);
                $booking = $payment->booking()->lockForUpdate()->first();
                if ($booking->booking_mode === 'instant' && $booking->status !== 'confirmed') {
                    $this->state->transition($booking, 'confirmed');
                    $this->maybeCreateCalendarEvent($booking);
                }
            });
        }

        return $payment;
    }

    public function releaseEscrow(Booking $booking): void
    {
        DB::transaction(function () use ($booking) {
            $this->escrow->release($booking, $booking->escrow_amount ?: $booking->amount);
            $this->state->transition($booking, 'released');
        });
    }

    public function refund(Payment $payment, float $amount): void
    {
        $driver = $this->gateways->fromModel($payment->gateway);
        $driver->refund($payment, $amount);
        $this->escrow->refund($payment, $amount);
    }

    protected function maybeCreateCalendarEvent(Booking $booking): void
    {
        if (! $this->calendar || ! $booking->provider) {
            return;
        }

        $token = $booking->provider->serviceCalendarTokens()->first();
        if (! $token) {
            return;
        }

        $created = $this->calendar->createEvent($token, [
            'summary' => 'Booking #'.$booking->id.' - '.$booking->service?->title,
            'description' => 'Customer: '.$booking->customer?->name,
            'start' => [
                'dateTime' => optional($booking->scheduled_start)->toAtomString(),
                'timeZone' => $booking->timezone ?? 'UTC',
            ],
            'end' => [
                'dateTime' => optional($booking->scheduled_end)->toAtomString(),
                'timeZone' => $booking->timezone ?? 'UTC',
            ],
        ]);
        if ($created && is_string($created)) {
            $booking->calendar_event_id = $created;
            $booking->save();
        }
    }
}
