<?php

namespace App\Domains\Event\Actions\UpdateParticipant;

use App\Enumerations\EfzStatus;
use App\Mail\ParticipantCocMails\ParticipantCocCompleteMail;
use App\Mail\ParticipantCocMails\ParticipantCocInvalidMail;
use App\Mail\ParticipantPaymentMails\ParticipantPaymentMissingPaymentMail;
use App\Mail\ParticipantPaymentMails\ParticipantPaymentOverpaidMail;
use App\Mail\ParticipantPaymentMails\ParticipantPaymentPaidMail;
use App\Models\EventParticipant;
use App\Providers\MissingPaymentProvider;
use App\ValueObjects\Amount;
use DateTime;
use Illuminate\Support\Facades\Mail;

class UpdateParticipantCommand {
    private UpdateParticipantResponse $response;
    function __construct(public UpdateParticipantRequest $request) {
    }

    public function execute() {
        $this->response = new UpdateParticipantResponse();

        $p = clone($this->request->participant);

        $p->firstname         = $this->request->firstname;
        $p->lastname          = $this->request->lastname;
        $p->nickname          = $this->request->nickname;
        $p->address_1         = $this->request->address_1;
        $p->address_2         = $this->request->address_2;
        $p->postcode          = $this->request->postcode;
        $p->city              = $this->request->city;
        $p->local_group       = $this->request->localgroup;
        $p->birthday          = DateTime::createFromFormat('Y-m-d', $this->request->birthday);
        $p->email_1           = $this->request->email_1;
        $p->phone_1           = $this->request->phone_1;
        $p->contact_person    = $this->request->contact_person;
        $p->email_2           = $this->request->email_2;
        $p->phone_2           = $this->request->phone_2;
        $p->arrival_date      = DateTime::createFromFormat('Y-m-d', $this->request->arrival);
        $p->departure_date    = DateTime::createFromFormat('Y-m-d', $this->request->departure);
        $p->participation_type = $this->request->participationType;
        $p->eating_habit      = $this->request->eatingHabit;
        $p->payment_method    = $this->request->paymentMethod;
        $p->allergies         = $this->request->allergies;
        $p->intolerances      = $this->request->intolerances;
        $p->medications       = $this->request->medications;
        $p->first_aid_permission = $this->request->extendedFirstAid;
        $p->swimming_permission  = $this->request->swimmingPermission;
        $p->tetanus_vaccination  = $this->request->tetanusVaccination !== null
            ? DateTime::createFromFormat('Y-m-d', $this->request->tetanusVaccination)
            : null;
        $p->notes             = $this->request->notes;
        $p->amount_paid       = Amount::fromString($this->request->amountPaid);
        $p->amount            = Amount::fromString($this->request->amountExpected);
        $p->efz_status        = $this->request->cocStatus;

        if (
            MissingPaymentProvider::calculateMissingPayment(amountPaid: $p->amount_paid, amountToPay: $p->amount)->getAmount()
            !==
            MissingPaymentProvider::calculateMissingPayment(amountPaid: $this->request->participant->amount_paid, amountToPay: $this->request->participant->amount)->getAmount()
        ) {
            $this->handleAmountChanges($p);
        }

        if (
            $p->efz_status !== $this->request->participant->efz_status
        ) {
            $this->handleCocStatusChange($p);
        }


        $p->save();

        if ($this->request->participationOptions !== null) {
            $this->syncParticipationOptions($p);
        }

        if ($this->request->selectedAddons !== null) {
            $this->syncAddons($p);
        }

        $this->response->success = true;
        $this->response->participant = $p;
        return $this->response;
    }

    /**
     * Ersetzt die zugeordneten Zusätze (Event-Addons) anhand der Auswahl. Bewusst **ohne Preis/Betrag**
     * (amount 0): der Teilnahmebeitrag wird in den Details ohnehin manuell gesetzt. Es wird nur der Titel als
     * Snapshot gespeichert, damit die Zuordnung sichtbar bleibt.
     */
    private function syncAddons(EventParticipant $participant): void
    {
        $participant->selectedAddons()->delete();

        $selection = $this->request->selectedAddons ?? [];

        foreach ($participant->event?->addons ?? [] as $addon) {
            $key = $addon['key'] ?? null;
            if ($key === null || empty($selection[$key])) {
                continue;
            }

            $participant->selectedAddons()->create([
                'tenant' => $participant->tenant,
                'event_id' => $participant->event_id,
                'addon_key' => $key,
                'title' => $addon['title'] ?? $key,
                'price' => 0,
                'flat' => (bool)($addon['flat'] ?? false),
                'days' => 0,
                'amount' => 0,
            ]);
        }
    }

    /**
     * Ersetzt die zugeordneten Teilnahmeoptionen anhand der übergebenen Antworten. Nachträgliche Zuordnung im
     * Back-Office ist lückenlos möglich: nur gegen die Event-Definition gültige Antworten werden gespeichert,
     * leere/entfallene Antworten werden abgeräumt. Frage-/Options-Label werden als Snapshot mitgeschrieben.
     */
    private function syncParticipationOptions(EventParticipant $participant): void
    {
        $participant->selectedOptions()->delete();

        $answers = $this->request->participationOptions ?? [];

        foreach ($participant->event?->participation_options ?? [] as $question) {
            $key = $question['key'] ?? null;
            if ($key === null) {
                continue;
            }

            $value = $answers[$key] ?? null;
            if ($value === null || $value === '') {
                continue;
            }

            $option = null;
            foreach ($question['options'] ?? [] as $candidate) {
                if (($candidate['value'] ?? null) === $value) {
                    $option = $candidate;
                    break;
                }
            }

            if ($option === null) {
                continue;
            }

            $label = trim((string)($question['label'] ?? ''));

            $participant->selectedOptions()->create([
                'tenant' => $participant->tenant,
                'event_id' => $participant->event_id,
                'question_key' => $key,
                'question_label' => $label !== '' ? $label : ($question['title'] ?? $key),
                'value' => $value,
                'value_label' => $option['label'] ?? $value,
            ]);
        }
    }

    private function handleAmountChanges(EventParticipant $participant) {
        $this->response->amountPaid = $participant->amount_paid;
        $this->response->amountExpected = $participant->amount;

        $amountToPay = MissingPaymentProvider::calculateMissingPayment(
            amountPaid: $participant->amount_paid,
            amountToPay: $participant->amount,
        );

        switch (true) {
            case $amountToPay->getAmount() > 0:
                Mail::to($participant->email_1)->send(new ParticipantPaymentMissingPaymentMail(
                    participant: $participant,
                ));

                if ($participant->email_2 !== null) {
                    Mail::to($participant->email_2)->send(new ParticipantPaymentMissingPaymentMail(
                        participant: $participant,
                    ));
                }
                break;
            case $amountToPay->getAmount() < 0:
                Mail::to($participant->email_1)->send(new ParticipantPaymentOverpaidMail(
                    participant: $participant,
                ));

                if ($participant->email_2 !== null) {
                    Mail::to($participant->email_2)->send(new ParticipantPaymentOverpaidMail(
                        participant: $participant,
                    ));
                }
                break;
            default:
                Mail::to($participant->email_1)->send(new ParticipantPaymentPaidMail(
                    participant: $participant,
                ));

                if ($participant->email_2 !== null) {
                    Mail::to($participant->email_2)->send(new ParticipantPaymentPaidMail(
                        participant: $participant,
                    ));
                }
        }
    }

    private function handleCocStatusChange(EventParticipant $participant) {
        $this->response->cocStatus = $participant->efzStatus()->first();

        switch ($participant->efzStatus()->first()->slug) {
            case EfzStatus::EFZ_STATUS_CHECKED_VALID:
            case EfzStatus::EFZ_STATUS_NOT_REQUIRED:
                Mail::to($participant->email_1)->send(new ParticipantCocCompleteMail(
                    participant: $participant,
                ));
                break;

            case EfzStatus::EFZ_STATUS_CHECKED_INVALID:
                Mail::to($participant->email_1)->send(new ParticipantCocInvalidMail(
                    participant: $participant,
                ));
                break;
        }
    }
}
