<?php

namespace App\Http\Controllers\Admin;

use App\Enums\AppointmentStatus;
use App\Http\Controllers\Controller;
use App\Http\Requests\StoreAppointmentRequest;
use App\Http\Requests\UpdateAppointmentRequest;
use App\Models\Appointment;
use App\Models\Customer;
use App\Models\Service;
use App\Models\StaffProfile;
use App\Services\Appointments\AppointmentService;
use App\Services\Tenancy\CurrentBusiness;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\View\View;

class AppointmentController extends Controller
{
    public function __construct(private readonly AppointmentService $appointmentService)
    {
    }

    public function index(Request $request, CurrentBusiness $currentBusiness): View
    {
        $business = $currentBusiness->get() ?? abort(403);
        $filters = $request->only(['q', 'status', 'staff_profile_id', 'service_id', 'date', 'sort']);
        $appointmentsQuery = $this->appointmentQuery($business->id, $filters);

        match ($filters['sort'] ?? 'upcoming') {
            'oldest' => $appointmentsQuery->orderBy('starts_at'),
            'newest' => $appointmentsQuery->latest('starts_at'),
            default => $appointmentsQuery->orderBy('starts_at'),
        };

        $appointments = $appointmentsQuery->paginate(10)->withQueryString();

        return view('admin.appointments.index', [
            'appointments' => $appointments,
            'filters' => $filters,
            'stats' => $this->stats($business->id),
            ...$this->formOptions($business->id),
        ]);
    }

    public function calendar(Request $request, CurrentBusiness $currentBusiness): View
    {
        $business = $currentBusiness->get() ?? abort(403);
        $date = Carbon::parse($request->input('date', today()->toDateString()))->startOfDay();
        $filters = $request->only(['q', 'staff_profile_id', 'date']);
        $filters['date'] = $date->toDateString();

        $appointments = $this->appointmentQuery($business->id, $filters)
            ->whereDate('starts_at', $date->toDateString())
            ->orderBy('starts_at')
            ->get();

        return view('admin.appointments.calendar', [
            'appointments' => $appointments,
            'selectedDate' => $date,
            'filters' => $filters,
            'calendarHours' => range(8, 18),
            ...$this->formOptions($business->id),
        ]);
    }

    public function store(StoreAppointmentRequest $request, CurrentBusiness $currentBusiness): RedirectResponse
    {
        $business = $currentBusiness->get() ?? abort(403);
        $appointment = $this->appointmentService->create($business, $request->payload(), $request->user());

        return redirect()
            ->route('admin.appointments.show', $appointment)
            ->with('status', 'Appointment created successfully.');
    }

    public function show(Appointment $appointment, CurrentBusiness $currentBusiness): View
    {
        $business = $this->authorizeAppointment($appointment, $currentBusiness);
        $appointment->load([
            'customer',
            'service',
            'staffProfile',
            'invoice',
            'statusLogs.changedBy',
        ]);

        return view('admin.appointments.show', [
            'appointment' => $appointment,
            'statusLogs' => $appointment->statusLogs()->with('changedBy')->latest()->get(),
            ...$this->formOptions($business->id),
        ]);
    }

    public function update(UpdateAppointmentRequest $request, Appointment $appointment, CurrentBusiness $currentBusiness): RedirectResponse
    {
        $business = $this->authorizeAppointment($appointment, $currentBusiness);
        $appointment = $this->appointmentService->update($business, $appointment, $request->payload(), $request->user());

        return redirect()
            ->route('admin.appointments.show', $appointment)
            ->with('status', 'Appointment updated successfully.');
    }

    public function destroy(Appointment $appointment, CurrentBusiness $currentBusiness): RedirectResponse
    {
        $business = $this->authorizeAppointment($appointment, $currentBusiness);
        $this->appointmentService->delete($business, $appointment);

        return redirect()
            ->route('admin.appointments.index')
            ->with('status', 'Appointment deleted successfully.');
    }

    private function appointmentQuery(int $businessId, array $filters): Builder
    {
        return Appointment::query()
            ->forBusiness($businessId)
            ->with(['customer', 'service', 'staffProfile'])
            ->when($filters['q'] ?? null, function (Builder $query, string $search): void {
                $query->where(function (Builder $query) use ($search): void {
                    $query->where('booking_number', 'like', "%{$search}%")
                        ->orWhereHas('customer', function (Builder $customerQuery) use ($search): void {
                            $customerQuery->where('name', 'like', "%{$search}%")
                                ->orWhere('phone', 'like', "%{$search}%")
                                ->orWhere('email', 'like', "%{$search}%");
                        })
                        ->orWhereHas('service', fn (Builder $serviceQuery) => $serviceQuery->where('name', 'like', "%{$search}%"))
                        ->orWhereHas('staffProfile', fn (Builder $staffQuery) => $staffQuery->where('name', 'like', "%{$search}%"));
                });
            })
            ->when($filters['status'] ?? null, fn (Builder $query, string $status) => $query->where('status', $status))
            ->when($filters['staff_profile_id'] ?? null, fn (Builder $query, string $staffId) => $query->where('staff_profile_id', $staffId))
            ->when($filters['service_id'] ?? null, fn (Builder $query, string $serviceId) => $query->where('service_id', $serviceId))
            ->when($filters['date'] ?? null, fn (Builder $query, string $date) => $query->whereDate('starts_at', $date));
    }

    private function stats(int $businessId): array
    {
        return [
            'today' => Appointment::query()->forBusiness($businessId)->whereDate('starts_at', today())->count(),
            'remaining_today' => Appointment::query()->forBusiness($businessId)->whereDate('starts_at', today())->where('starts_at', '>=', now())->count(),
            'confirmed_week' => Appointment::query()->forBusiness($businessId)->where('status', AppointmentStatus::Confirmed->value)->whereBetween('starts_at', [now()->startOfWeek(), now()->endOfWeek()])->count(),
            'pending' => Appointment::query()->forBusiness($businessId)->where('status', AppointmentStatus::Pending->value)->count(),
            'no_show_7_days' => Appointment::query()->forBusiness($businessId)->where('status', AppointmentStatus::NoShow->value)->where('starts_at', '>=', now()->subDays(7))->count(),
        ];
    }

    private function formOptions(int $businessId): array
    {
        return [
            'customers' => Customer::query()->forBusiness($businessId)->orderBy('name')->get(),
            'services' => Service::query()->forBusiness($businessId)->where('status', Service::STATUS_ACTIVE)->orderBy('name')->get(),
            'staffProfiles' => StaffProfile::query()->forBusiness($businessId)->where('status', StaffProfile::STATUS_ACTIVE)->orderBy('name')->get(),
            'statusOptions' => AppointmentStatus::options(),
            'paymentStatusOptions' => [
                'unpaid' => 'Unpaid',
                'paid' => 'Paid',
                'partial' => 'Partial',
                'pending' => 'Pending',
            ],
        ];
    }

    private function authorizeAppointment(Appointment $appointment, CurrentBusiness $currentBusiness)
    {
        $business = $currentBusiness->get() ?? abort(403);

        abort_unless((int) $appointment->business_id === (int) $business->id, 404);

        return $business;
    }
}
