<?php

namespace Database\Seeders;

use App\Enums\AppointmentStatus;
use App\Enums\PaymentStatus;
use App\Enums\QueueStatus;
use App\Enums\UserRole;
use App\Enums\UserStatus;
use App\Models\Appointment;
use App\Models\AppointmentStatusHistory;
use App\Models\Assistant;
use App\Models\AssistantAssignment;
use App\Models\Doctor;
use App\Models\DoctorLocationAssignment;
use App\Models\DoctorWorkingHour;
use App\Models\Location;
use App\Models\Organization;
use App\Models\Patient;
use App\Models\QueueCounter;
use App\Models\QueueEntry;
use App\Models\QueueStatusHistory;
use App\Models\User;
use Carbon\CarbonImmutable;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;

class ClinicDemoSeeder extends Seeder
{
    public function run(): void
    {
        $this->call(EssentialSeeder::class);

        $organization = Organization::where('slug', 'smart-clinic')->firstOrFail();
        $timezone = $organization->timezone;

        DB::transaction(function () use ($organization, $timezone) {
            $admin = User::updateOrCreate(
                ['organization_id' => $organization->id, 'email' => 'admin@smartclinic.test'],
                [
                    'name' => 'Smart Clinic Admin',
                    'password' => Hash::make('password'),
                    'role' => UserRole::ADMIN,
                    'status' => UserStatus::ACTIVE,
                    'email_verified_at' => now(),
                ]
            );

            $location1 = Location::firstOrCreate(
                ['organization_id' => $organization->id, 'name' => 'Main Clinic'],
                ['type' => 'clinic', 'city' => 'Lahore', 'phone' => '+924200000001', 'is_active' => true]
            );
            $location2 = Location::firstOrCreate(
                ['organization_id' => $organization->id, 'name' => 'City Hospital OPD'],
                ['type' => 'hospital', 'city' => 'Lahore', 'phone' => '+924200000002', 'is_active' => true]
            );

            [$doctor1, $doctorUser1] = $this->doctor(
                $organization->id,
                'dr.ali@smartclinic.test',
                'Dr. Ali Raza',
                'Cardiology',
                'FCPS Cardiology'
            );
            [$doctor2, $doctorUser2] = $this->doctor(
                $organization->id,
                'dr.sara@smartclinic.test',
                'Dr. Sara Khan',
                'General Medicine',
                'FCPS Medicine'
            );

            $assignment1 = DoctorLocationAssignment::updateOrCreate(
                ['doctor_id' => $doctor1->id, 'location_id' => $location1->id],
                [
                    'organization_id' => $organization->id,
                    'consultation_fee' => 2500,
                    'followup_fee' => 1500,
                    'slot_minutes' => 20,
                    'booking_window_days' => 30,
                    'cancellation_cutoff_minutes' => 120,
                    'is_active' => true,
                ]
            );
            $assignment2 = DoctorLocationAssignment::updateOrCreate(
                ['doctor_id' => $doctor1->id, 'location_id' => $location2->id],
                [
                    'organization_id' => $organization->id,
                    'consultation_fee' => 3000,
                    'followup_fee' => 1800,
                    'slot_minutes' => 30,
                    'booking_window_days' => 30,
                    'cancellation_cutoff_minutes' => 120,
                    'is_active' => true,
                ]
            );
            $assignment3 = DoctorLocationAssignment::updateOrCreate(
                ['doctor_id' => $doctor2->id, 'location_id' => $location1->id],
                [
                    'organization_id' => $organization->id,
                    'consultation_fee' => 2000,
                    'followup_fee' => 1200,
                    'slot_minutes' => 20,
                    'booking_window_days' => 30,
                    'cancellation_cutoff_minutes' => 120,
                    'is_active' => true,
                ]
            );

            foreach ([$assignment1, $assignment2, $assignment3] as $assignment) {
                for ($weekday = 1; $weekday <= 6; $weekday++) {
                    DoctorWorkingHour::firstOrCreate(
                        [
                            'doctor_location_assignment_id' => $assignment->id,
                            'weekday' => $weekday,
                            'start_time' => '09:00:00',
                            'end_time' => '13:00:00',
                        ],
                        ['organization_id' => $organization->id, 'is_active' => true]
                    );
                    DoctorWorkingHour::firstOrCreate(
                        [
                            'doctor_location_assignment_id' => $assignment->id,
                            'weekday' => $weekday,
                            'start_time' => '17:00:00',
                            'end_time' => '20:00:00',
                        ],
                        ['organization_id' => $organization->id, 'is_active' => true]
                    );
                }
            }

            [$assistant1, $assistantUser1] = $this->assistant(
                $organization->id,
                'assistant.one@smartclinic.test',
                'Assistant One',
                'AST-001'
            );
            [$assistant2, $assistantUser2] = $this->assistant(
                $organization->id,
                'assistant.two@smartclinic.test',
                'Assistant Two',
                'AST-002'
            );

            AssistantAssignment::updateOrCreate(
                [
                    'assistant_id' => $assistant1->id,
                    'doctor_id' => $doctor1->id,
                    'location_id' => $location1->id,
                ],
                [
                    'organization_id' => $organization->id,
                    'is_active' => true,
                    'can_create_patient' => true,
                    'can_book_appointment' => true,
                    'can_check_in' => true,
                    'can_reschedule' => true,
                    'can_cancel' => true,
                    'can_edit_patient' => true,
                    'can_collect_payment' => true,
                ]
            );

            AssistantAssignment::updateOrCreate(
                [
                    'assistant_id' => $assistant2->id,
                    'doctor_id' => $doctor2->id,
                    'location_id' => $location1->id,
                ],
                [
                    'organization_id' => $organization->id,
                    'is_active' => true,
                    'can_create_patient' => true,
                    'can_book_appointment' => true,
                    'can_check_in' => true,
                    'can_reschedule' => true,
                    'can_cancel' => true,
                    'can_edit_patient' => true,
                    'can_collect_payment' => false,
                ]
            );

            $patients = collect([
                ['name' => 'Ahmed Khan', 'phone' => '+923001234567', 'wa' => true],
                ['name' => 'Fatima Noor', 'phone' => '+923111234567', 'wa' => true],
                ['name' => 'Usman Tariq', 'phone' => '+923221234567', 'wa' => false],
            ])->map(function ($row, $index) use ($organization, $assistantUser1) {
                $patient = Patient::firstOrCreate(
                    ['organization_id' => $organization->id, 'phone' => $row['phone']],
                    [
                        'name' => $row['name'],
                        'normalized_phone' => $row['phone'],
                        'has_whatsapp' => $row['wa'],
                        'whatsapp' => $row['wa'] ? $row['phone'] : null,
                        'city' => 'Lahore',
                        'created_by' => $assistantUser1->id,
                        'updated_by' => $assistantUser1->id,
                    ]
                );

                if (! $patient->patient_code) {
                    $patient->update([
                        'patient_code' => 'PAT-'.str_pad((string) (10000 + $patient->id), 5, '0', STR_PAD_LEFT),
                    ]);
                }

                return $patient;
            });

            $today = CarbonImmutable::now($timezone)->startOfDay();
            $times = ['09:00', '09:20', '09:40'];
            $statuses = [
                AppointmentStatus::WITH_DOCTOR,
                AppointmentStatus::WAITING,
                AppointmentStatus::WAITING,
            ];

            $entries = collect();

            foreach ($patients as $i => $patient) {
                $startLocal = CarbonImmutable::parse($today->toDateString().' '.$times[$i], $timezone);
                $appointment = Appointment::firstOrCreate(
                    [
                        'organization_id' => $organization->id,
                        'patient_id' => $patient->id,
                        'doctor_id' => $doctor1->id,
                        'location_id' => $location1->id,
                        'scheduled_start_at' => $startLocal->utc(),
                    ],
                    [
                        'doctor_location_assignment_id' => $assignment1->id,
                        'created_by_user_id' => $assistantUser1->id,
                        'created_by_assistant_id' => $assistant1->id,
                        'scheduled_end_at' => $startLocal->addMinutes(20)->utc(),
                        'type' => 'new_consultation',
                        'reason' => 'Demo appointment',
                        'base_fee' => 2500,
                        'discount_amount' => 0,
                        'final_fee' => 2500,
                        'payment_status' => PaymentStatus::PENDING,
                        'status' => $statuses[$i],
                    ]
                );

                if (! $appointment->appointment_code) {
                    $appointment->update([
                        'appointment_code' => 'APT-'.str_pad((string) (12000 + $appointment->id), 5, '0', STR_PAD_LEFT),
                    ]);
                }

                AppointmentStatusHistory::firstOrCreate(
                    [
                        'appointment_id' => $appointment->id,
                        'to_status' => $statuses[$i]->value,
                    ],
                    [
                        'from_status' => AppointmentStatus::CONFIRMED->value,
                        'reason' => 'Demo queue fixture',
                        'changed_by_user_id' => $assistantUser1->id,
                        'created_at' => now(),
                    ]
                );

                $token = 4 + $i;
                $scopeKey = "doctor:{$doctor1->id}:location:{$location1->id}";
                $entry = QueueEntry::updateOrCreate(
                    [
                        'organization_id' => $organization->id,
                        'queue_scope_key' => $scopeKey,
                        'queue_date' => $today->toDateString(),
                        'token_number' => $token,
                    ],
                    [
                        'appointment_id' => $appointment->id,
                        'patient_id' => $patient->id,
                        'doctor_id' => $doctor1->id,
                        'location_id' => $location1->id,
                        'status' => $i === 0 ? QueueStatus::WITH_DOCTOR : QueueStatus::WAITING,
                        'arrival_at' => $startLocal->subMinutes(15)->utc(),
                        'sent_to_doctor_at' => $i === 0 ? $startLocal->utc() : null,
                        'created_by_user_id' => $assistantUser1->id,
                    ]
                );

                QueueStatusHistory::firstOrCreate(
                    [
                        'queue_entry_id' => $entry->id,
                        'event_type' => 'seed.fixture',
                    ],
                    [
                        'to_status' => $entry->status->value,
                        'new_token_number' => $token,
                        'changed_by_user_id' => $assistantUser1->id,
                        'created_at' => now(),
                    ]
                );

                $entries->push($entry);
            }

            QueueCounter::updateOrCreate(
                [
                    'organization_id' => $organization->id,
                    'queue_scope_key' => "doctor:{$doctor1->id}:location:{$location1->id}",
                    'queue_date' => $today->toDateString(),
                ],
                [
                    'last_allocated_token' => 6,
                    'current_served_token' => 4,
                ]
            );
        });
    }

    private function doctor(int $organizationId, string $email, string $name, string $specialization, string $qualification): array
    {
        $user = User::updateOrCreate(
            ['organization_id' => $organizationId, 'email' => $email],
            [
                'name' => $name,
                'password' => Hash::make('password'),
                'role' => UserRole::DOCTOR,
                'status' => UserStatus::ACTIVE,
                'email_verified_at' => now(),
            ]
        );

        $doctor = Doctor::updateOrCreate(
            ['user_id' => $user->id],
            [
                'organization_id' => $organizationId,
                'specialization' => $specialization,
                'qualification' => $qualification,
                'experience_years' => 8,
                'is_active' => true,
            ]
        );

        return [$doctor, $user];
    }

    private function assistant(int $organizationId, string $email, string $name, string $code): array
    {
        $user = User::updateOrCreate(
            ['organization_id' => $organizationId, 'email' => $email],
            [
                'name' => $name,
                'password' => Hash::make('password'),
                'role' => UserRole::ASSISTANT,
                'status' => UserStatus::ACTIVE,
                'email_verified_at' => now(),
            ]
        );

        $assistant = Assistant::updateOrCreate(
            ['user_id' => $user->id],
            [
                'organization_id' => $organizationId,
                'employee_code' => $code,
                'is_active' => true,
            ]
        );

        return [$assistant, $user];
    }
}
