<?php

namespace App\Domain\Event\Services;

use App\Domain\Event\Enums\FormFieldType;
use App\Domain\Event\Enums\RegistrationStatus;
use App\Domain\Event\Models\Event;
use App\Domain\Event\Models\EventRegistration;
use App\Domain\Identity\Models\Person;
use App\Domain\Shared\Exceptions\DomainException;
use App\Models\User;
use Illuminate\Support\Facades\DB;

class EventRegistrationService
{
    public function register(Event $event, array $formData, array $meta): EventRegistration
    {
        if (! $event->isRegistrationOpen()) {
            throw new DomainException('التسجيل مغلق لهذا الحدث');
        }

        if (! $event->hasCapacity()) {
            throw new DomainException('تم اكتمال العدد المسموح به');
        }

        $this->checkUniqueness($event, $formData);

        return DB::transaction(function () use ($event, $formData, $meta) {
            $personId = $this->matchPerson($meta['phone'], $meta['email'] ?? null, $event->academy_id);

            $registration = EventRegistration::create([
                'academy_id' => $event->academy_id,
                'event_id' => $event->id,
                'person_id' => $personId,
                'registration_number' => $this->generateRegistrationNumber($event),
                'status' => 'pending',
                'form_data' => $formData,
                'registrant_name' => $meta['name'],
                'registrant_phone' => $meta['phone'],
                'registrant_email' => $meta['email'] ?? null,
                'ip_address' => $meta['ip'] ?? null,
            ]);

            $event->increment('registrations_count');

            return $registration;
        });
    }

    public function confirm(EventRegistration $registration, User $admin): EventRegistration
    {
        if ($registration->status !== RegistrationStatus::Pending) {
            throw new DomainException('لا يمكن تأكيد هذا التسجيل');
        }

        $registration->update([
            'status' => 'confirmed',
            'confirmed_at' => now(),
        ]);

        return $registration->fresh();
    }

    public function cancel(EventRegistration $registration, User $admin, ?string $reason = null): EventRegistration
    {
        if (in_array($registration->status, [RegistrationStatus::Cancelled, RegistrationStatus::Attended])) {
            throw new DomainException('لا يمكن إلغاء هذا التسجيل');
        }

        DB::transaction(function () use ($registration, $reason) {
            $registration->update([
                'status' => 'cancelled',
                'cancelled_at' => now(),
                'admin_notes' => $reason ? ($registration->admin_notes ? $registration->admin_notes . "\n" : '') . "سبب الإلغاء: {$reason}" : $registration->admin_notes,
            ]);

            $registration->event->decrement('registrations_count');
        });

        return $registration->fresh();
    }

    public function markAttended(EventRegistration $registration, User $admin): EventRegistration
    {
        if ($registration->status === RegistrationStatus::Cancelled) {
            throw new DomainException('لا يمكن تسجيل حضور تسجيل ملغي');
        }

        $registration->update([
            'status' => 'attended',
            'attended_at' => now(),
        ]);

        return $registration->fresh();
    }

    public function bulkConfirm(array $ids, User $admin): int
    {
        $count = 0;
        $registrations = EventRegistration::whereIn('id', $ids)->where('status', 'pending')->get();

        foreach ($registrations as $reg) {
            $this->confirm($reg, $admin);
            $count++;
        }

        return $count;
    }

    public function bulkCancel(array $ids, User $admin): int
    {
        $count = 0;
        $registrations = EventRegistration::whereIn('id', $ids)
            ->whereNotIn('status', ['cancelled', 'attended'])
            ->get();

        foreach ($registrations as $reg) {
            $this->cancel($reg, $admin);
            $count++;
        }

        return $count;
    }

    public function validateFormData(Event $event, array $submitted): array
    {
        $rules = [];
        $messages = [];

        foreach ($event->form_fields as $field) {
            $type = FormFieldType::tryFrom($field['type']);

            if (! $type || ! $type->isInputField()) {
                continue;
            }

            if ($this->isFieldHiddenByCondition($field, $submitted, $event->form_fields)) {
                continue;
            }

            $key = "fields.{$field['key']}";
            $fieldRules = [];

            if ($field['is_required'] ?? false) {
                $fieldRules[] = $field['type'] === 'terms' ? 'accepted' : 'required';
                $messages["{$key}.required"] = "حقل {$field['label']} مطلوب";
                $messages["{$key}.accepted"] = "يجب الموافقة على {$field['label']}";
            } else {
                $fieldRules[] = 'nullable';
            }

            if ($type && $type->baseValidation()) {
                foreach (explode('|', $type->baseValidation()) as $rule) {
                    if ($rule === 'accepted' && !($field['is_required'] ?? false)) {
                        continue;
                    }
                    $fieldRules[] = $rule;
                }
            }

            if ($type && $type->hasOptions() && !empty($field['options'])) {
                $values = array_column($field['options'], 'value');
                if ($field['allow_other'] ?? false) {
                    // Don't restrict to options if "Other" is allowed
                } elseif ($type === FormFieldType::MultiSelect) {
                    $fieldRules[] = 'array';
                    $rules["{$key}.*"] = 'in:' . implode(',', $values);
                } else {
                    $fieldRules[] = 'in:' . implode(',', $values);
                }
            }

            if (!empty($field['min_length'])) {
                $fieldRules[] = 'min:' . (int) $field['min_length'];
            }
            if (!empty($field['max_length'])) {
                $fieldRules[] = 'max:' . (int) $field['max_length'];
            }

            if (!empty($field['min_value']) && $type === FormFieldType::Number) {
                $fieldRules[] = 'min:' . (float) $field['min_value'];
            }
            if (!empty($field['max_value']) && $type === FormFieldType::Number) {
                $fieldRules[] = 'max:' . (float) $field['max_value'];
            }

            if (!empty($field['pattern'])) {
                $fieldRules[] = 'regex:/' . $field['pattern'] . '/';
                $messages["{$key}.regex"] = $field['pattern_message'] ?? "تنسيق {$field['label']} غير صحيح";
            }

            $rules[$key] = implode('|', array_filter($fieldRules));
        }

        return ['rules' => $rules, 'messages' => $messages];
    }

    public function checkUniqueness(Event $event, array $formData): void
    {
        foreach ($event->form_fields as $field) {
            if (empty($field['is_unique'])) {
                continue;
            }

            $value = $formData[$field['key']] ?? null;
            if (empty($value)) {
                continue;
            }

            $exists = EventRegistration::withoutGlobalScopes()
                ->where('event_id', $event->id)
                ->where('status', '!=', 'cancelled')
                ->whereRaw("form_data->>? = ?", [$field['key'], $value])
                ->exists();

            if ($exists) {
                throw new DomainException("تم التسجيل مسبقاً بنفس {$field['label']}");
            }
        }
    }

    private function isFieldHiddenByCondition(array $field, array $submitted, array $allFields): bool
    {
        if (empty($field['condition_field']) || empty($field['condition_value'])) {
            return false;
        }

        $conditionFieldKey = $field['condition_field'];
        $conditionValue = $field['condition_value'];
        $submittedValue = $submitted[$conditionFieldKey] ?? null;

        if ($submittedValue === null) {
            return true;
        }

        if (is_array($submittedValue)) {
            return !in_array($conditionValue, $submittedValue);
        }

        return (string) $submittedValue !== (string) $conditionValue;
    }

    public function matchPerson(string $phone, ?string $email, int $academyId): ?int
    {
        if (empty($phone) && empty($email)) {
            return null;
        }

        $person = Person::withoutGlobalScopes()
            ->where('academy_id', $academyId)
            ->where(function ($q) use ($phone, $email) {
                if ($phone) {
                    $q->where('phone', $phone);
                }
                if ($email) {
                    $q->orWhere('email', $email);
                }
            })
            ->first();

        return $person?->id;
    }

    public function generateRegistrationNumber(Event $event): string
    {
        $prefix = 'EVT-' . now()->format('ym');
        $lastNumber = EventRegistration::withoutGlobalScopes()
            ->where('academy_id', $event->academy_id)
            ->where('registration_number', 'like', $prefix . '%')
            ->max('registration_number');

        if ($lastNumber) {
            $seq = (int) substr($lastNumber, -4) + 1;
        } else {
            $seq = 1;
        }

        return $prefix . '-' . str_pad($seq, 4, '0', STR_PAD_LEFT);
    }
}
