<?php

namespace App\Http\Controllers\Tenant;

use App\Enums\BookingStatus;
use App\Http\Controllers\Controller;
use App\Models\Booking;
use App\Models\Employee;
use App\Models\Setting;
use App\Support\Tenant;
use App\Services\Booking\BookingService;
use Carbon\Carbon;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;

class BookingController extends Controller
{
    public function index(Request $request, BookingService $service): View
    {
        $service->archiveExpired();

        $status = $request->query('status', 'all');
        $validStatuses = collect(BookingStatus::cases())->map->value->all();
        if ($status !== 'all' && ! in_array($status, $validStatuses, true)) {
            $status = 'all';
        }

        $statusCounts = Booking::query()
            ->selectRaw('status, COUNT(*) as total')
            ->groupBy('status')
            ->pluck('total', 'status')
            ->map(fn ($value) => (int) $value)
            ->all();

        $statusFilters = collect(BookingStatus::cases())
            ->reject(fn (BookingStatus $case) => $case === BookingStatus::Archived)
            ->map(fn (BookingStatus $case) => [
                'value' => $case->value,
                'label' => $case->label(),
                'color' => $case->color(),
                'count' => $statusCounts[$case->value] ?? 0,
            ])
            ->values()
            ->all();

        $bookings = Booking::with(['customer', 'service', 'employee'])
            ->when($status === 'all', fn ($query) => $query->where('status', '!=', BookingStatus::Archived->value))
            ->when($status !== 'all', fn ($query) => $query->where('status', $status))
            ->latest('scheduled_at')
            ->paginate(20)
            ->withQueryString();

        $totalVisibleCount = array_sum(array_filter(
            $statusCounts,
            fn ($count, $key) => $key !== BookingStatus::Archived->value,
            ARRAY_FILTER_USE_BOTH
        ));
        $archivedCount = $statusCounts[BookingStatus::Archived->value] ?? 0;

        return view('tenant.bookings.index', compact('bookings', 'status', 'statusFilters', 'totalVisibleCount', 'archivedCount'));
    }

    public function agenda(): View
    {
        $companyId = Tenant::id();
        $today = Carbon::today();

        $todayBookings = Booking::with(['customer:id,first_name,last_name', 'service:id,name', 'employee:id,first_name,last_name,color'])
            ->whereDate('scheduled_at', $today)
            ->whereIn('status', ['pending', 'confirmed', 'completed'])
            ->orderBy('scheduled_at')
            ->limit(6)
            ->get();

        $weekBookings = Booking::query()
            ->whereBetween('scheduled_at', [now()->startOfWeek(), now()->endOfWeek()])
            ->whereIn('status', ['pending', 'confirmed', 'completed', 'cancelled'])
            ->get();

        $statusBreakdown = [
            'Confirmadas' => $weekBookings->filter(fn ($booking) => $booking->status?->value === 'confirmed')->count(),
            'Pendientes' => $weekBookings->filter(fn ($booking) => $booking->status?->value === 'pending')->count(),
            'Atendidas' => $weekBookings->filter(fn ($booking) => $booking->status?->value === 'completed')->count(),
            'Canceladas' => $weekBookings->filter(fn ($booking) => $booking->status?->value === 'cancelled')->count(),
        ];

        $settings = [
            'slot_minutes' => (int) Setting::get('agenda_slot_minutes', 30, $companyId),
            'show_weekends' => (bool) Setting::get('agenda_show_weekends', true, $companyId),
        ];

        return view('tenant.bookings.agenda', compact('todayBookings', 'statusBreakdown', 'settings'));
    }

    public function create(): View
    {
        return view('tenant.bookings.create');
    }

    public function show(Booking $booking): View
    {
        $this->authorize('view', $booking);
        return view('tenant.bookings.show', compact('booking'));
    }

    public function edit(Booking $booking): View
    {
        $this->authorize('update', $booking);
        return view('tenant.bookings.show', compact('booking'));
    }

    public function cancel(Request $request, Booking $booking, BookingService $service): RedirectResponse
    {
        $this->authorize('cancel', $booking);

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

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

        return redirect()->route('tenant.bookings.show', $booking)
            ->with('success', 'Reserva cancelada correctamente.');
    }

    public function confirm(Booking $booking, BookingService $service): RedirectResponse
    {
        $this->authorize('update', $booking);

        if ($booking->status !== BookingStatus::Pending) {
            return back()->with('warning', 'Solo puedes confirmar reservas pendientes.');
        }

        $service->confirm($booking);

        return back()->with('success', 'Reserva confirmada correctamente.');
    }

    public function complete(Request $request, Booking $booking, BookingService $service): RedirectResponse
    {
        $this->authorize('update', $booking);

        if (! in_array($booking->status, [BookingStatus::Pending, BookingStatus::Confirmed], true)) {
            return back()->with('warning', 'Solo puedes marcar como atendidas reservas pendientes o confirmadas.');
        }

        $data = $request->validate([
            'payment_method' => ['required', 'in:cash,card,transfer,online,other'],
        ]);

        $service->complete($booking, $data['payment_method']);

        return back()->with('success', 'Reserva marcada como atendida.');
    }

    public function noShow(Booking $booking, BookingService $service): RedirectResponse
    {
        $this->authorize('update', $booking);

        if (! in_array($booking->status, [BookingStatus::Pending, BookingStatus::Confirmed], true)) {
            return back()->with('warning', 'Solo puedes marcar como no asistidas reservas pendientes o confirmadas.');
        }

        $service->markNoShow($booking);

        return back()->with('success', 'Reserva marcada como no asistida.');
    }

    public function move(Request $request, Booking $booking): JsonResponse
    {
        $this->authorize('update', $booking);

        if (! $booking->status?->isActive()) {
            return response()->json([
                'message' => 'Solo puedes mover reservas pendientes o confirmadas.',
            ], 422);
        }

        $data = $request->validate([
            'scheduled_at' => ['required', 'date'],
            'end_at' => ['nullable', 'date', 'after:scheduled_at'],
            'employee_id' => ['nullable', 'integer', 'exists:employees,id'],
        ]);

        $start = Carbon::parse($data['scheduled_at'])->seconds(0);
        $end = filled($data['end_at'] ?? null)
            ? Carbon::parse($data['end_at'])->seconds(0)
            : $start->copy()->addMinutes($booking->duration_minutes);

        if ($end->lessThanOrEqualTo($start)) {
            return response()->json([
                'message' => 'La hora final debe ser mayor que la hora inicial.',
            ], 422);
        }

        $duration = max(5, (int) $start->diffInMinutes($end));
        $employeeId = $data['employee_id'] ?? $booking->employee_id;
        $employee = null;

        if ($employeeId) {
            $employee = Employee::query()->whereKey($employeeId)->first();

            if (
                ! $employee
                || (int) $employee->company_id !== (int) $booking->company_id
                || ! $employee->is_active
                || ! $employee->is_bookable
            ) {
                return response()->json([
                    'message' => 'El profesional seleccionado no está disponible para reservas.',
                ], 422);
            }

            $booking->loadMissing('service');

            if (! $booking->service?->employees()->whereKey($employee->id)->exists()) {
                return response()->json([
                    'message' => 'Ese profesional no tiene asignado este servicio.',
                ], 422);
            }

            $overlaps = Booking::query()
                ->whereKeyNot($booking->id)
                ->where('employee_id', $employee->id)
                ->active()
                ->where('scheduled_at', '<', $end)
                ->whereRaw('DATE_ADD(scheduled_at, INTERVAL duration_minutes MINUTE) > ?', [$start])
                ->exists();

            if ($overlaps) {
                return response()->json([
                    'message' => 'Ese horario ya está ocupado para el profesional seleccionado.',
                ], 422);
            }
        }

        $booking->update([
            'employee_id' => $employee?->id,
            'branch_id' => $employee?->branch_id ?? $booking->branch_id,
            'booking_date' => $start->toDateString(),
            'start_time' => $start->format('H:i:s'),
            'end_time' => $end->format('H:i:s'),
            'scheduled_at' => $start,
            'duration_minutes' => $duration,
        ]);

        return response()->json([
            'message' => 'Reserva actualizada correctamente.',
            'booking' => [
                'id' => $booking->uuid,
                'scheduled_at' => $start->toIso8601String(),
                'end_at' => $end->toIso8601String(),
                'duration_minutes' => $duration,
                'employee_id' => $employee?->id,
            ],
        ]);
    }

    /** Endpoint JSON para FullCalendar. */
    public function eventsJson(Request $request)
    {
        $start = $request->query('start');
        $end   = $request->query('end');

        $bookings = Booking::with([
            'customer:id,first_name,last_name,phone',
            'service:id,name',
            'employee:id,first_name,last_name,color',
        ])
            ->whereIn('status', ['pending', 'confirmed', 'completed'])
            ->whereBetween('scheduled_at', [$start ?? now()->subDays(30), $end ?? now()->addDays(60)])
            ->get();

        return $bookings->map(fn ($b) => [
            'id'    => $b->uuid,
            'url'   => route('tenant.bookings.show', $b),
            'title' => $b->service?->name ?? 'Reserva',
            'start' => $b->scheduled_at?->toIso8601String(),
            'end'   => $b->scheduled_at?->copy()->addMinutes($b->duration_minutes)->toIso8601String(),
            'color' => $b->employee?->color ?? '#4f46e5',
            'editable' => (bool) $b->status?->isActive(),
            'startEditable' => (bool) $b->status?->isActive(),
            'durationEditable' => (bool) $b->status?->isActive(),
            'extendedProps' => [
                'code'     => $b->code,
                'employee_id' => $b->employee_id,
                'service_id' => $b->service_id,
                'customer' => trim(($b->customer?->first_name ?? '').' '.($b->customer?->last_name ?? '')),
                'employee' => $b->employee?->fullName(),
                'phone'    => $b->customer?->phone,
                'status'   => $b->status?->value,
                'statusLabel' => $b->status?->label(),
                'duration' => $b->duration_minutes,
                'amount' => number_format((float) $b->total_amount, 0, ',', '.').' '.$b->currency,
                'service' => $b->service?->name,
                'color' => $b->employee?->color ?? '#4f46e5',
            ],
        ]);
    }
}
