<?php

namespace App\Services;

use App\Enums\UserRole;
use App\Models\Appointment;
use App\Models\QueueEntry;
use App\Models\User;
use Carbon\CarbonImmutable;

class DashboardService
{
    public function __construct(private readonly AccessService $access) {}

    public function for(User $user): array
    {
        $user->loadMissing(['organization', 'doctor.locationAssignments.location', 'assistant.assignments.doctor.user', 'assistant.assignments.location', 'patient']);
        $timezone = $user->organization?->timezone ?? config('clinic.default_timezone');
        $today = CarbonImmutable::now($timezone);
        $from = $today->startOfDay()->utc();
        $to = $today->endOfDay()->utc();

        return match ($user->role) {
            UserRole::ASSISTANT => $this->staffDashboard($user, $from, $to, true),
            UserRole::DOCTOR => $this->staffDashboard($user, $from, $to, false),
            UserRole::PATIENT => $this->patientDashboard($user),
            UserRole::ADMIN => [
                'kind' => 'admin',
                'admin_dashboard_endpoint' => '/api/v1/admin/reports/dashboard',
            ],
        };
    }

    private function staffDashboard(User $user, CarbonImmutable $from, CarbonImmutable $to, bool $assistant): array
    {
        $appointments = $this->access->scopeAppointments(
            Appointment::query()->with(['patient', 'doctor.user', 'location']),
            $user
        )->whereBetween('scheduled_start_at', [$from, $to]);

        $queue = $this->access->scopeQueue(
            QueueEntry::query()->with(['patient', 'doctor.user', 'location', 'appointment']),
            $user
        )->whereDate('queue_date', $from->setTimezone($user->organization->timezone)->toDateString());

        $upcoming = (clone $appointments)->where('scheduled_start_at', '>=', now())->orderBy('scheduled_start_at')->limit(10)->get();
        $queueRows = (clone $queue)->orderBy('token_number')->get();

        $payload = [
            'kind' => $assistant ? 'assistant' : 'doctor',
            'today' => [
                'appointments' => (clone $appointments)->count(),
                'checked_in' => (clone $queue)->where('status', 'checked_in')->count(),
                'waiting' => (clone $queue)->where('status', 'waiting')->count(),
                'with_doctor' => (clone $queue)->where('status', 'with_doctor')->count(),
                'completed' => (clone $queue)->where('status', 'completed')->count(),
            ],
            'upcoming_schedule' => $upcoming,
            'queue' => $queueRows,
        ];

        if ($assistant) {
            $payload['assignments'] = $user->assistant?->assignments
                ->where('is_active', true)
                ->map(fn ($assignment) => [
                    'id' => $assignment->id,
                    'doctor_id' => $assignment->doctor_id,
                    'doctor_name' => $assignment->doctor?->user?->name,
                    'location_id' => $assignment->location_id,
                    'location_name' => $assignment->location?->name,
                ])->values()->all() ?? [];
        } else {
            $payload['locations'] = $user->doctor?->locationAssignments
                ->where('is_active', true)
                ->map(fn ($assignment) => [
                    'assignment_id' => $assignment->id,
                    'location_id' => $assignment->location_id,
                    'location_name' => $assignment->location?->name,
                ])->values()->all() ?? [];

            $payload['recent_patients'] = $this->access->scopeAppointments(
                Appointment::query()->with('patient'),
                $user
            )->whereNotNull('patient_id')
                ->orderByDesc('scheduled_start_at')
                ->limit(30)
                ->get()
                ->unique('patient_id')
                ->take(10)
                ->map(fn ($appointment) => $appointment->patient)
                ->filter()
                ->values();
        }

        return $payload;
    }

    private function patientDashboard(User $user): array
    {
        if (! $user->patient) {
            return ['kind' => 'patient', 'profile' => null, 'upcoming_appointment' => null, 'past_appointments_count' => 0, 'unread_notifications' => 0];
        }

        $appointments = $this->access->scopeAppointments(
            Appointment::query()->with(['doctor.user', 'location']),
            $user
        );

        return [
            'kind' => 'patient',
            'profile' => $user->patient,
            'upcoming_appointment' => (clone $appointments)
                ->where('scheduled_start_at', '>=', now())
                ->whereNotIn('status', ['cancelled', 'no_show'])
                ->orderBy('scheduled_start_at')
                ->first(),
            'past_appointments_count' => (clone $appointments)
                ->where('scheduled_start_at', '<', now())
                ->count(),
            'unread_notifications' => $user->unreadNotifications()->count(),
        ];
    }
}
