<?php

namespace App\Http\Controllers\Public;

use App\Http\Controllers\Controller;
use App\Models\Booking;
use App\Services\Booking\BookingService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;

class BookingCancellationController extends Controller
{
    public function lookup(): View
    {
        return view('public.booking-cancel-lookup');
    }

    public function find(Request $request): RedirectResponse
    {
        $data = $request->validate([
            'code' => ['required', 'string', 'max:40'],
            'phone' => ['required', 'string', 'max:25'],
        ]);

        $booking = Booking::withoutGlobalScopes()
            ->with(['customer'])
            ->where('code', trim($data['code']))
            ->first();

        if (! $booking || ! $this->phoneMatches($data['phone'], (string) $booking->customer?->phone)) {
            return back()
                ->withInput()
                ->with('error', 'No encontramos una reserva con ese código y celular.');
        }

        return redirect()->route('public.bookings.cancel.show', $booking);
    }

    public function show(Booking $booking): View
    {
        $booking->loadMissing(['company', 'customer', 'service', 'employee']);

        return view('public.booking-cancel', [
            'booking' => $booking,
            'canCancel' => $this->canCustomerCancel($booking),
            'message' => $this->cancellationMessage($booking),
        ]);
    }

    public function cancel(Request $request, Booking $booking, BookingService $service): RedirectResponse
    {
        $booking->loadMissing(['company', 'customer', 'service']);

        $data = $request->validate([
            'phone' => ['required', 'string', 'max:25'],
            'reason' => ['nullable', 'string', 'max:255'],
        ]);

        if (! $this->phoneMatches($data['phone'], (string) $booking->customer?->phone)) {
            return back()
                ->withInput()
                ->with('error', 'El teléfono no coincide con el registrado en la reserva.');
        }

        if (! $this->canCustomerCancel($booking)) {
            return back()->with('error', $this->cancellationMessage($booking));
        }

        $service->cancel($booking, $data['reason'] ?? 'Cancelada por el cliente');

        return redirect()->route('public.bookings.cancel.show', $booking)
            ->with('success', 'Tu reserva fue cancelada correctamente.');
    }

    protected function canCustomerCancel(Booking $booking): bool
    {
        if (! $booking->company?->allow_customer_cancel) {
            return false;
        }

        if (! $booking->isCancellable()) {
            return false;
        }

        if (! $booking->scheduled_at?->isFuture()) {
            return false;
        }

        $limit = $this->cancellationLimitMinutes($booking);

        return $limit <= 0 || now()->diffInMinutes($booking->scheduled_at, false) >= $limit;
    }

    protected function cancellationMessage(Booking $booking): string
    {
        if (! $booking->company?->allow_customer_cancel) {
            return 'Este negocio no permite cancelaciones en línea. Comunícate directamente con el negocio.';
        }

        if (! $booking->isCancellable()) {
            return 'Esta reserva ya no se puede cancelar porque su estado es '.$booking->status->label().'.';
        }

        if (! $booking->scheduled_at?->isFuture()) {
            return 'Esta reserva ya pasó y no puede cancelarse en línea.';
        }

        $limit = $this->cancellationLimitMinutes($booking);
        if ($limit > 0 && now()->diffInMinutes($booking->scheduled_at, false) < $limit) {
            return 'Solo puedes cancelar en línea con al menos '.$this->formatCancellationLimit($limit).' de anticipación.';
        }

        return 'Puedes cancelar esta reserva en línea.';
    }

    protected function cancellationLimitMinutes(Booking $booking): int
    {
        $serviceLimit = $booking->service?->cancellation_min_notice_minutes;

        if ($serviceLimit !== null) {
            return max(0, (int) $serviceLimit);
        }

        return max(0, (int) ($booking->company?->cancellation_hours_limit ?? 0) * 60);
    }

    protected function formatCancellationLimit(int $minutes): string
    {
        if ($minutes >= 60 && $minutes % 60 === 0) {
            $hours = (int) ($minutes / 60);

            return $hours === 1 ? '1 hora' : "{$hours} horas";
        }

        return $minutes === 1 ? '1 minuto' : "{$minutes} minutos";
    }

    protected function phoneMatches(string $given, string $stored): bool
    {
        return preg_replace('/\D+/', '', $given) === preg_replace('/\D+/', '', $stored);
    }
}
