<?php

namespace App\Http\Controllers\Tenant;

use App\Domain\Booking\BookingService;
use App\Domain\Booking\Exceptions\SlotUnavailableException;
use App\Http\Controllers\Controller;
use App\Http\Requests\RescheduleAppointmentRequest;
use App\Http\Requests\StoreAppointmentRequest;
use App\Http\Requests\UpdateAppointmentStatusRequest;
use App\Models\Appointment;
use App\Models\Dentist;
use App\Models\Patient;
use App\Support\ClinicTime;
use Carbon\CarbonImmutable;
use Illuminate\Http\Request;

class AppointmentController extends Controller
{
    public function __construct(protected BookingService $booking)
    {
    }

    /** Calendar page (FullCalendar fetches /appointments/feed). */
    public function index()
    {
        $this->authorize('viewAny', Appointment::class);

        return view('appointments.calendar', [
            'dentists' => Dentist::active()->orderBy('name')->get(),
            'patients' => Patient::orderBy('full_name')->limit(500)->get(['id', 'full_name', 'phone']),
        ]);
    }

    /** JSON event feed for the calendar. */
    public function feed(Request $request)
    {
        $this->authorize('viewAny', Appointment::class);

        $tz = ClinicTime::timezone();

        $events = Appointment::with(['patient:id,full_name', 'dentist:id,name,color'])
            ->between(
                CarbonImmutable::parse($request->query('start', 'now'))->utc(),
                CarbonImmutable::parse($request->query('end', '+1 month'))->utc(),
            )
            ->when($request->integer('dentist_id'), fn ($q, $id) => $q->where('dentist_id', $id))
            ->get()
            ->map(fn (Appointment $a) => [
                'id' => $a->id,
                'title' => $a->patient->full_name,
                'start' => $a->starts_at->setTimezone($tz)->toIso8601String(),
                'end' => $a->ends_at->setTimezone($tz)->toIso8601String(),
                'backgroundColor' => $a->dentist->color,
                'borderColor' => $a->status->color(),
                'editable' => $a->status->holdsSlot() && ! $a->starts_at->isPast(),
                'extendedProps' => [
                    'status' => $a->status->value,
                    'statusLabel' => $a->status->label(),
                    'dentist' => $a->dentist->name,
                    'reason' => $a->reason,
                    'patientId' => $a->patient_id,
                ],
            ]);

        return response()->json($events);
    }

    public function store(StoreAppointmentRequest $request)
    {
        try {
            $appointment = $this->booking->book(
                dentist: Dentist::findOrFail($request->dentist_id),
                patient: Patient::findOrFail($request->patient_id),
                startUtc: ClinicTime::toUtc($request->starts_at),
                source: 'reception',
                reason: $request->reason,
                createdBy: $request->user()->id,
            );
        } catch (SlotUnavailableException $e) {
            return $this->conflict($request, $e);
        }

        return $request->wantsJson()
            ? response()->json(['data' => ['id' => $appointment->id]], 201)
            : back()->with('status', __('Appointment booked.'));
    }

    /** Drag & drop / reschedule. */
    public function update(RescheduleAppointmentRequest $request, Appointment $appointment)
    {
        $this->authorize('update', $appointment);

        try {
            $this->booking->reschedule($appointment, ClinicTime::toUtc($request->starts_at));
        } catch (SlotUnavailableException $e) {
            return $this->conflict($request, $e);
        }

        return $request->wantsJson()
            ? response()->json(['data' => ['id' => $appointment->id]])
            : back()->with('status', __('Appointment moved.'));
    }

    public function updateStatus(UpdateAppointmentStatusRequest $request, Appointment $appointment)
    {
        $this->authorize('update', $appointment);

        $this->booking->changeStatus(
            $appointment,
            $request->enum('status', \App\Enums\AppointmentStatus::class),
            $request->cancellation_reason,
        );

        // "Set next visit": optionally create a recall when completing.
        if ($request->filled('recall_months') && $appointment->status === \App\Enums\AppointmentStatus::Completed) {
            \App\Models\Recall::create([
                'patient_id' => $appointment->patient_id,
                'dentist_id' => $appointment->dentist_id,
                'appointment_id' => $appointment->id,
                'due_date' => now()->addMonths($request->integer('recall_months'))->toDateString(),
                'reason' => $request->input('recall_reason', __('Routine check-up')),
                'status' => 'scheduled',
            ]);
        }

        return $request->wantsJson()
            ? response()->json(['data' => ['id' => $appointment->id, 'status' => $appointment->status->value]])
            : back()->with('status', __('Status updated.'));
    }

    protected function conflict(Request $request, SlotUnavailableException $e)
    {
        return $request->wantsJson()
            ? response()->json(['errors' => [['detail' => $e->getMessage()]]], 409)
            : back()->with('error', $e->getMessage());
    }
}
