<?php

namespace App\Services;

use App\Enums\AccountStatus;
use App\Enums\OrganizerPrimaryEventType;
use App\Mail\OrganizerApprovedMail;
use App\Mail\OrganizerRegistrationStaffNotificationMail;
use App\Mail\OrganizerRegistrationWelcomeMail;
use App\Mail\OrganizerRejectedMail;
use App\Models\Organizer;
use App\Models\Role;
use App\Models\User;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Mail;
use Throwable;

class OrganizerOnboardingService
{
    public function __construct(private readonly AuditLogger $auditLogger) {}

    /**
     * @param  array{name: string, email: string, password: string, organization_name: string, phone: string, primary_event_type: OrganizerPrimaryEventType}  $data
     */
    public function register(array $data): User
    {
        $user = DB::transaction(function () use ($data): User {
            $roleId = Role::query()->where('slug', Role::SLUG_ORGANIZADOR)->value('id');
            if ($roleId === null) {
                throw new \RuntimeException('Missing organizador role.');
            }

            $user = User::query()->create([
                'name' => $data['name'],
                'email' => $data['email'],
                'password' => Hash::make($data['password']),
                'role_id' => $roleId,
                'account_status' => AccountStatus::AwaitingReview,
                'organization_name' => $data['organization_name'],
                'phone' => $data['phone'],
                'rejection_reason' => null,
            ]);

            Organizer::query()->create([
                'user_id' => $user->id,
                'primary_event_type' => $data['primary_event_type'],
            ]);

            $this->auditLogger->log(
                'organizer.registered',
                'organizer_onboarding',
                __('Solicitud de registro de organizador: :email', ['email' => $user->email]),
                ['user_id' => $user->id],
                subject: $user,
            );

            return $user;
        });

        $this->notifyStaffOfOrganizerRegistration($user);
        $this->notifyApplicantRegistrationReceived($user);

        return $user;
    }

    private function notifyApplicantRegistrationReceived(User $organizerApplicant): void
    {
        try {
            Mail::to($organizerApplicant->email)->send(new OrganizerRegistrationWelcomeMail($organizerApplicant));
        } catch (Throwable $e) {
            report($e);
        }
    }

    /**
     * Aviso a super_admin, administrador y correos opcionales en config.
     */
    private function notifyStaffOfOrganizerRegistration(User $organizerApplicant): void
    {
        $emails = $this->organizerRegistrationStaffRecipientEmails();
        if ($emails === []) {
            return;
        }

        try {
            Mail::to($emails)->send(new OrganizerRegistrationStaffNotificationMail($organizerApplicant));
        } catch (Throwable $e) {
            report($e);
        }
    }

    /**
     * @return list<string>
     */
    private function organizerRegistrationStaffRecipientEmails(): array
    {
        $fromDb = User::query()
            ->whereHas('role', static fn ($q) => $q->whereIn('slug', [
                Role::SLUG_SUPER_ADMIN,
                Role::SLUG_ADMINISTRADOR,
            ]))
            ->pluck('email')
            ->map(static fn ($e) => is_string($e) ? strtolower(trim($e)) : '')
            ->filter()
            ->all();

        $extraRaw = config('mail.organizer_registration_notify');
        $extraList = [];
        if (is_string($extraRaw) && $extraRaw !== '') {
            foreach (explode(',', $extraRaw) as $part) {
                $e = strtolower(trim($part));
                if ($e !== '' && filter_var($e, FILTER_VALIDATE_EMAIL)) {
                    $extraList[] = $e;
                }
            }
        }

        return array_values(array_unique([...$fromDb, ...$extraList]));
    }

    public function approve(User $organizerUser, User $actor): void
    {
        DB::transaction(function () use ($organizerUser, $actor): void {
            $organizerUser->forceFill([
                'account_status' => AccountStatus::Approved,
                'rejection_reason' => null,
            ])->save();

            Mail::to($organizerUser->email)->send(new OrganizerApprovedMail($organizerUser));

            $this->auditLogger->log(
                'organizer.approved',
                'organizer_onboarding',
                __('Organizador aprobado: :email', ['email' => $organizerUser->email]),
                ['organizer_user_id' => $organizerUser->id, 'actor_id' => $actor->id],
                subject: $organizerUser,
                userId: $actor->id,
            );
        });
    }

    public function reject(User $organizerUser, User $actor, string $reason): void
    {
        $reason = trim($reason);

        DB::transaction(function () use ($organizerUser, $actor, $reason): void {
            $organizerUser->forceFill([
                'account_status' => AccountStatus::Rejected,
                'rejection_reason' => $reason,
            ])->save();

            Mail::to($organizerUser->email)->send(new OrganizerRejectedMail($organizerUser, $reason));

            $this->auditLogger->log(
                'organizer.rejected',
                'organizer_onboarding',
                __('Organizador rechazado: :email', ['email' => $organizerUser->email]),
                ['organizer_user_id' => $organizerUser->id, 'actor_id' => $actor->id],
                subject: $organizerUser,
                userId: $actor->id,
            );
        });
    }
}
