<?php

namespace App\Repositories;

use App\Enumerations\EatingHabit;
use App\Models\Event;
use App\Models\EventParticipant;
use Illuminate\Http\Request;

class EventParticipantRepository {
    public function getForList(Event $event, Request $request, bool $signedOffParticipants = false) : array {
        $participants = [];

        if (!$signedOffParticipants) {
            $allParticipants = $event->participants()->whereNull('unregistered_at');
        } else {
            $allParticipants = $event->participants()->whereNotNull('unregistered_at');
        }

        foreach ($allParticipants->orderBy('lastname')->orderBy('firstname')->get() as $participant) {
            $participants[] = $participant->toResource()->toArray($request);
        };

        return $participants;
    }

    public function getByIdentifier(string $identifier, EventRepository $eventRepository, bool $withPermissionCheck = true) : ?EventParticipant {
        $participant = EventParticipant::where('identifier', $identifier)->first();
        if ($participant === null) {
            return null;
        }

        if ($withPermissionCheck) {
            $event = $eventRepository->getById($participant->event_id);
            if ($event === null) {
                return null;
            }
        }
        return $participant;
    }

    public function groupByLocalGroup(Event $event, Request $request, ?string $filter = null) : array {
        $allParticipants = $this->getForList($event, $request);
        $participants = [];
        foreach ($allParticipants as $participant) {
            if ($filter === null || $participant['localgroup'] === $filter) {
                $participants[$participant['localgroup']][] = $participant;
            }
        }

        return $participants;
    }

    public function groupByParticipationType(Event $event, Request $request, ?string $filter = null) : array {
        $allParticipants = $this->getForList($event, $request);
        $participants = [];
        foreach ($allParticipants as $participant) {
            if ($filter === null || $participant['participationType'] === $filter) {
                $participants[$participant['participationType']][] = $participant;
            }
        }

        return $participants;
    }

    /**
     * Gruppiert die (aktiven) Teilnehmenden nach ihren gewählten Teilnahmeoptionen. Je am Event definierter
     * Frage entsteht eine Sektion mit Gruppen „Frage: Option" (in Definition-Reihenfolge); Teilnehmende ohne
     * Antwort landen unter „Frage: (keine Angabe)". Ein Teilnehmer erscheint je Frage einmal, kann also über
     * mehrere Fragen hinweg in mehreren Gruppen auftauchen. `$filter` liefert nur die eine passende Gruppe
     * (für die E-Mail-Auflösung in ByGroupController).
     */
    public function groupByParticipationOption(Event $event, Request $request, ?string $filter = null): array
    {
        $definitions = $event->participation_options ?? [];
        $allParticipants = $this->getForList($event, $request);
        $groups = [];

        foreach ($definitions as $question) {
            $questionKey = $question['key'] ?? null;
            if ($questionKey === null) {
                continue;
            }

            // Überschrift: bevorzugt der Titel, sonst die (längere) Frage bzw. der Key als Fallback.
            $questionLabel = trim((string)($question['title'] ?? '')) !== ''
                ? $question['title']
                : ($question['label'] ?? $questionKey);
            $noAnswerKey = $questionLabel . ': (keine Angabe)';

            // Vordefinierte Optionen in Reihenfolge: value => Gruppen-Key.
            $definedGroupKeys = [];
            foreach ($question['options'] ?? [] as $option) {
                $definedGroupKeys[$option['value']] = $questionLabel . ': ' . ($option['label'] ?? $option['value']);
            }

            $collected = [];
            foreach ($allParticipants as $participant) {
                $answer = null;
                foreach ($participant['participationOptions'] ?? [] as $selectedOption) {
                    if (($selectedOption['questionKey'] ?? null) === $questionKey) {
                        $answer = $selectedOption;
                        break;
                    }
                }

                if ($answer === null || ($answer['value'] ?? '') === '') {
                    $collected[$noAnswerKey][] = $participant;
                    continue;
                }

                // Bevorzugt die definierte Option; falls sie zwischenzeitlich entfernt wurde, den Snapshot nutzen.
                $groupKey = $definedGroupKeys[$answer['value']] ?? ($questionLabel . ': ' . ($answer['valueLabel'] ?? $answer['value']));
                $collected[$groupKey][] = $participant;
            }

            // Ausgabe je Frage: definierte Optionen zuerst (in Reihenfolge), dann Snapshot-Reste, „(keine Angabe)" ans Ende.
            foreach ($definedGroupKeys as $groupKey) {
                if (!empty($collected[$groupKey])) {
                    $groups[$groupKey] = $collected[$groupKey];
                    unset($collected[$groupKey]);
                }
            }

            foreach ($collected as $groupKey => $participants) {
                if ($groupKey === $noAnswerKey) {
                    continue;
                }
                $groups[$groupKey] = $participants;
            }

            if (!empty($collected[$noAnswerKey])) {
                $groups[$noAnswerKey] = $collected[$noAnswerKey];
            }
        }

        if ($filter !== null) {
            return array_key_exists($filter, $groups) ? [$filter => $groups[$filter]] : [];
        }

        return $groups;
    }

    /**
     * Gruppiert die (aktiven) Teilnehmenden nach ihren gebuchten Zusätzen (Event-Addons). Da Addons Ja/Nein
     * sind, entsteht eine Gruppe je Zusatz (Überschrift = Addon-Titel) mit den Teilnehmenden, die ihn gebucht
     * haben; ein Teilnehmer kann in mehreren Gruppen auftauchen. Nur nicht-leere Gruppen. Die Reihenfolge folgt
     * der Event-Definition; nachträglich entfernte Addons werden über den Snapshot-Titel weiterhin abgedeckt.
     */
    public function groupByAddon(Event $event, Request $request, ?string $filter = null): array
    {
        $allParticipants = $this->getForList($event, $request);

        // Gruppen-Reihenfolge aus der Event-Definition vorbelegen (leere Gruppen werden am Ende verworfen).
        $groups = [];
        foreach ($event->addons ?? [] as $addon) {
            $title = $addon['title'] ?? ($addon['key'] ?? null);
            if ($title !== null) {
                $groups[$title] = [];
            }
        }

        foreach ($allParticipants as $participant) {
            foreach ($participant['selectedAddons'] ?? [] as $selectedAddon) {
                $title = $selectedAddon['title'] ?? ($selectedAddon['addonKey'] ?? null);
                if ($title === null) {
                    continue;
                }
                $groups[$title][] = $participant;
            }
        }

        $groups = array_filter($groups, fn($group) => count($group) > 0);

        if ($filter !== null) {
            return array_key_exists($filter, $groups) ? [$filter => $groups[$filter]] : [];
        }

        return $groups;
    }

    public function getSignedOffParticipants(Event $event, Request $request, ?string $filter = null) : array {
        $allParticipants = $this->getForList($event, $request, true);
        $participants = [];
        foreach ($allParticipants as $participant) {
            if ($filter === null || $participant['participationType'] === $filter) {
                $participants[$participant['participationType']][] = $participant;
            }
        }

        return $participants;
    }

    public function getParticipantsWithMissingPayments(Event$event, Request $request) : array {
        $participants = [];
        foreach ($event->participants()->whereNull('unregistered_at')
                     ->whereColumn('amount', '<>', 'amount_paid')
                     ->get() as $participant) {
            $participants[] = $participant;
        };

        return $participants;
    }

    public function getParticipantsWithIntolerances(Event $event, Request $request) : array {
        $participants = [];
        foreach ($event->participants()->orderBy('lastname')->orderBy('firstname')->whereNotNull('intolerances')->whereNot('intolerances' , '=', '')->get() as $participant) {
            $participants[] = $participant->toResource()->toArray($request);
        };

        return $participants;
    }

    public function getKitchenOverview(Event $event) : array {
        $data = [];
        $participants = $event->participants()->orderBy('lastname')->orderBy('firstname')->get();


        for ($cur_date = $event->start_date; $cur_date <= $event->end_date; $cur_date->modify('+1 day')) {
            $dateKey = $cur_date->format('d.m.Y');

            $data[$dateKey] = [
                '1' => [],
                '2' => [],
                '3' => [],
            ];

            foreach (EatingHabit::all() as $eatingHabit) {
                $data[$dateKey]['1'][$eatingHabit->slug] = 0;
                $data[$dateKey]['2'][$eatingHabit->slug] = 0;
                $data[$dateKey]['3'][$eatingHabit->slug] = 0;
            }

            foreach ($participants as $participant) {
                $eatingHabitSlug = $participant->eating_habit;
                if ($eatingHabitSlug === null) {
                    continue;
                }

                $anreise = $participant->arrival_date;
                $abreise = $participant->departure_date;

                if ($anreise->getTimestamp() === $cur_date->getTimestamp()) {
                    switch ((int)$participant->arrival_eating) {
                        case 1:
                            $data[$dateKey]['3'][$eatingHabitSlug]++;
                            break;
                        case 2:
                            $data[$dateKey]['2'][$eatingHabitSlug]++;
                            $data[$dateKey]['3'][$eatingHabitSlug]++;
                            break;
                        case 3:
                            $data[$dateKey]['1'][$eatingHabitSlug]++;
                            $data[$dateKey]['2'][$eatingHabitSlug]++;
                            $data[$dateKey]['3'][$eatingHabitSlug]++;
                            break;
                    }
                } elseif ($abreise->getTimestamp() === $cur_date->getTimestamp()) {
                    switch ((int)$participant->departure_eating) {
                        case 1:
                            $data[$dateKey]['1'][$eatingHabitSlug]++;
                            break;
                        case 2:
                            $data[$dateKey]['1'][$eatingHabitSlug]++;
                            $data[$dateKey]['2'][$eatingHabitSlug]++;
                            break;
                        case 3:
                            $data[$dateKey]['1'][$eatingHabitSlug]++;
                            $data[$dateKey]['2'][$eatingHabitSlug]++;
                            $data[$dateKey]['3'][$eatingHabitSlug]++;
                            break;
                    }
                } else {
                    $data[$dateKey]['1'][$eatingHabitSlug]++;
                    $data[$dateKey]['2'][$eatingHabitSlug]++;
                    $data[$dateKey]['3'][$eatingHabitSlug]++;
                }
            }
        }

        return $data;
    }

    public function getMailAddresses(array $participants) : array {
        $mailAddresses = [];
        foreach ($participants as $participant) {
            if (!in_array($participant['email_1'], $mailAddresses)) {
                $mailAddresses[] = $participant['email_1'];
            }

            if ($participant['email_2'] !== null && !in_array($participant['email_2'], $mailAddresses)) {
                $mailAddresses[] = $participant['email_2'];
            }
        }
        return $mailAddresses;
    }

    public function getMyParticipations(?int $maxEvents = null) : array {
        $participations = [];
        $user = auth()->user();
        if ($user === null) {
            return $participations;
        }

        $request = new \Illuminate\Http\Request();

        $query = EventParticipant::where('user_id', $user->id)
            ->whereNull('unregistered_at')
            ->whereHas('event', function ($q) {
                $q->where('end_date', '>=', now());
            })
            ->with(['event'])
            ->join('events', 'event_participants.event_id', '=', 'events.id')
            ->orderBy('events.start_date', 'asc')
            ->select('event_participants.*');

        if ($maxEvents !== null) {
            $query->limit($maxEvents);
        }

        foreach ($query->get() as $participant) {
            $participations[] = $participant->toResource()->toArray($request);
        }

        return $participations;
    }

    public function getMyParticipationByIdentifier(string $identifier) : ?EventParticipant {
        $user = auth()->user();
        if ($user === null) {
            return null;
        }

        return EventParticipant::where('identifier', $identifier)
            ->where('tenant', app('tenant')->slug)
            ->where('user_id', $user->id)
            ->whereNull('unregistered_at')
            ->first();
    }
}
