<?php

namespace App\Resources;

use App\Enumerations\ParticipationFeeType;
use App\Enumerations\ParticipationType;
use App\Enumerations\VatPricingMode;
use App\Models\Event;
use App\ValueObjects\Amount;
use DateTime;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;

class EventResource extends JsonResource{
    private Event $event;

    public function __construct(Event $event) {
        $this->event = $event;
    }

    public function toArray(Request $request) : array
    {
        $duration = $this->event->end_date->diff($this->event->start_date)->days + 1;

        $returnArray = [
            'id' => $this->event->id,
            'name' => $this->event->name,
            'identifier' => $this->event->identifier,
            'url' => 'https://' . app('tenant')->url . '/event/' . $this->event->identifier . '/signup',
            'urlShort' => 'https://' . app('tenant')->url . '/event/' . $this->event->identifier,
            'location' => $this->event->location,
            'postalCode' => $this->event->postal_code,
            'email' => $this->event->email,
            'accountOwner' => $this->event->account_owner,
            'accountIban' => $this->event->account_iban,
            'alcoholicsAge' => $this->event->alcoholics_age,
            'sendWeeklyReports' => $this->event->send_weekly_report,
            'registrationAllowed' => $this->event->registration_allowed && new \DateTime() <= $this->event->start_date,
            'archived' => $this->event->archived,
            'earlyBirdEnd' => ['internal' => $this->event->early_bird_end->format('Y-m-d'), 'formatted' => $this->event->early_bird_end->format('d.m.Y')],
            'registrationFinalEnd' => ['internal' => $this->event->registration_final_end->format('Y-m-d'), 'formatted' => $this->event->registration_final_end->format('d.m.Y')],
            'refundAfterEarlyBirdEnd' => 100 - $this->event->early_bird_end_amount_increase,
        ];

        $returnArray['swimmingPermissions'] = \App\Enumerations\SwimmingPermission::query()
            ->get()
            ->map(fn ($permission) => [
                'slug' => $permission->slug,
                'name' => $permission->name,
            ])
            ->toArray();

        $returnArray['firstAidPermissions'] = \App\Enumerations\FirstAidPermission::query()
            ->get()
            ->map(fn ($permission) => [
                'slug' => $permission->slug,
                'name' => $permission->name,
            ])
            ->toArray();


        foreach ([
            ParticipationType::PARTICIPATION_TYPE_PARTICIPANT,
            ParticipationType::PARTICIPATION_TYPE_TEAM,
            ParticipationType::PARTICIPATION_TYPE_VOLUNTEER,
            ParticipationType::PARTICIPATION_TYPE_OTHER,
        ] as $participationType) {
            $_t =$this->getParticipants($participationType);
            $returnArray['participants'][$participationType] = $_t;
            $returnArray['count' . ucfirst($participationType)] = $_t['count'];
        }

        $returnArray['nameShort'] = $returnArray['name'];
        if (strlen($returnArray['nameShort']) > 15) {
            $returnArray['nameShort'] = substr($returnArray['nameShort'], 8, 13) . '...';
        }

        $returnArray['siblingReduction'] = $this->event->sibling_reduction ?? true;
        $returnArray['costUnit'] = new CostUnitResource($this->event->costUnit()->first())->toArray(true);
        $returnArray['solidarityPayment'] = $this->event->participation_fee_type === ParticipationFeeType::PARTICIPATION_FEE_TYPE_SOLIDARITY;
        $returnArray['payPerDay'] = $this->event->pay_per_day;
        $returnArray['maxAmount'] = $this->event->total_max_amount->getFormattedAmount();
        $returnArray['arrivalDefault'] = $this->event->start_date->format('Y-m-d');
        $returnArray['departureDefault'] = $this->event->end_date->format('Y-m-d');
        $returnArray['eventBegin'] = $this->event->start_date->format('d.m.Y');
        $returnArray['eventBeginInternal'] = $this->event->start_date;
        $returnArray['eventEnd'] = $this->event->end_date->format('d.m.Y');
        $returnArray['eventEndInternal'] = $this->event->end_date;
        $returnArray['duration'] = $duration;
        $returnArray['totalParticipantCount'] = $this->event->participants()->count();

        $returnArray['supportPersonIndex'] = $this->event->support_per_person->toString();
        $returnArray['supportPersonValue'] = $this->event->support_per_person->getAmount();
        $returnArray['supportPerson'] = $this->calculateSupportPerPerson($returnArray['participants']);

        $returnArray['income'] = $this->calculateIncomes($returnArray['participants'], $returnArray['supportPerson']['amount']);


        $totalBalanceReal = new Amount(0, 'Euro');
        $totalBalanceExpected = new Amount(0, 'Euro');
        $totalBalanceEstimated = new Amount(0, 'Euro');

        $totalBalanceReal->addAmount($returnArray['income']['real']['amount']);
        $totalBalanceExpected->addAmount($returnArray['income']['expected']['amount']);
        $totalBalanceEstimated->addAmount($returnArray['income']['expected']['amount']);

        $totalBalanceReal->subtractAmount($returnArray['costUnit']['overAllAmount']['value']);
        $totalBalanceExpected->subtractAmount($returnArray['costUnit']['overAllAmount']['value']);
        $totalBalanceEstimated->subtractAmount($returnArray['costUnit']['overAllEstimatedAmount']['value']);
        $returnArray['totalBalance'] = [
                'real' => [
                    'value' => $totalBalanceReal->getAmount(),
                    'readable' => $totalBalanceReal->toString(),
                ], 'expected' => [
                    'value' => $totalBalanceExpected->getAmount(),
                    'readable' => $totalBalanceExpected->toString(),
                ],
            'estimated' => [
                'value' => $totalBalanceEstimated->getAmount(),
                'readable' => $totalBalanceEstimated->toString(),
            ]
        ];

        $returnArray['flatSupport'] = $this->event->support_flat->toString();

        $returnArray['flatSupportEdit'] = $this->event->support_flat->getFormattedAmount();
        $returnArray['supportPersonEdit'] = $this->event->support_per_person->getFormattedAmount();
        $returnArray['managers'] = $this->event->eventManagers()->get()->map(fn($user) => new UserResource($user))->toArray();

        $returnArray['supportPersonCalced'] = $this->event->support_per_person->toString();
        $returnArray['contributingLocalGroups'] = $this->event->localGroups
            ->map(fn ($localGroup) => new LocalGroupResource($localGroup)->toArray($request))
            ->toArray();

        $returnArray['eatingHabits'] = $this->event->eatingHabits()->get()->map(
            fn($eatingHabit) => new EatingHabitResource($eatingHabit))->toArray();

        $returnArray['paymentMethods'] = $this->event->paymentMethods()->get()->map(
            fn($paymentMethod) => new AvailablePaymentMethodResource($paymentMethod)->toArray($request))->toArray();

        // Veranstaltungsspezifische Pflicht-Auswahlfragen (Teilnahmeoptionen); leeres Array => Schritt entfällt.
        $returnArray['participationOptions'] = $this->event->participation_options ?? [];

        // Buchbare Zusätze (Event-Addons); leeres Array => Schritt entfällt. Preis 0 => "Kostenfrei".
        $returnArray['addons'] = collect($this->event->addons ?? [])->map(function ($addon) {
            $price = (float)($addon['price'] ?? 0);
            return [
                'key' => $addon['key'] ?? '',
                'title' => $addon['title'] ?? '',
                'description' => $addon['description'] ?? '',
                'price' => $price,
                'priceReadable' => new Amount($price, 'Euro')->toString(),
                'free' => $price <= 0,
                'flat' => (bool)($addon['flat'] ?? false),
            ];
        })->toArray();


        $returnArray['participationTypes'] = [];

        $multiplier = $this->getMultiplier();

        for ($i = 1; $i <= 4; $i++) {
            $returnArray['participationFee_' . $i] = [
                'active' => false,
                'name' => '',
                'description' => '',
                'amount_standard' => null,
                'amount_reduced' => null,
                'amount_solidarity' => null,
                'type' => null,
            ];

            if ($this->event->{'participation_fee_' . $i} === null) {
                continue;
            }

            $participationFee = $this->event->{'participationFee' . $i}()->first();

            $feeType = [
                'active' => true,
                'amount_standard_edit' => $participationFee->amount_standard->getFormattedAmount(),
                'amount_standard' => [
                    'internal' => [
                        'amount' => $participationFee->amount_standard->getAmount(),
                        'currency' => $participationFee->amount_standard->getCurrency(),
                    ],
                    'readable' => $participationFee->amount_standard->multiply($multiplier)->toString(),
                ],
                'amount_reduced_edit' => $participationFee->amount_reduced === null ? null : $participationFee->amount_reduced->getFormattedAmount(),
                'amount_reduced' => $participationFee->amount_reduced === null
                    ? null
                    : [
                        'internal' => [
                            'amount' => $participationFee->amount_reduced->getAmount(),
                            'currency' => $participationFee->amount_reduced->getCurrency(),
                        ],
                        'readable' => $participationFee->amount_reduced->multiply($multiplier)->toString(),
                    ],
                'amount_solidarity_edit' => $participationFee->amount_solidarity === null ? null : $participationFee->amount_solidarity->getFormattedAmount(),
                'amount_solidarity' => $participationFee->amount_solidarity === null
                    ? null
                    : [
                        'internal' => [
                            'amount' => $participationFee->amount_solidarity->getAmount(),
                            'currency' => $participationFee->amount_solidarity->getCurrency(),
                        ],
                        'readable' => $participationFee->amount_solidarity->multiply($multiplier)->toString(),
                    ],
                'name' => $participationFee->name,
                'description' => $participationFee->description,
                'type' => (new ParticipationTypeResource($participationFee->type()->first()))->toArray($request),
            ];

            $returnArray['participationFee_' . $i] = $feeType;
            $returnArray['participationTypes'][] = $feeType;

        }

        return $returnArray;
    }

    public function getMultiplier() : float {
        $earlyBirdEnd = $this->event->early_bird_end;
        if ($earlyBirdEnd > now()) {
            return 1;
        }

        return 1 + $this->event->early_bird_end_amount_increase / 100;
    }

    public function calculateSupportPerPerson(array $participants) : array {
        if ($this->event->support_per_person === null) {
            return [
                'amount' => new Amount(0, 'Euro'),
                'readable' => '0,00'
            ];
        }

        $amount = $this->event->support_per_person;
        $multiplier = 0;



        foreach ($participants as $participationType => $participantData) {
            if (isset($participantData['participants']) && count($participantData['participants']) > 0) {
                foreach ($participantData['participants'] as $participant) {
                    $multiplier += $participant->toResource()->toArray(new Request())['presenceDays']['support'];
                }
            }
        }

        $amount->multiply($multiplier);

        return [
            'amount' => $amount,
            'readable' => $amount->toString()
        ];


    }

    public function calculateIncomes(array $participants, Amount $supportPerPerson) : array {
        $expectedAmount = new Amount($supportPerPerson->getAmount(), 'Euro');
        $expectedAmount->addAmount($this->event->support_flat);

        $realAmount = new Amount($supportPerPerson->getAmount(), 'Euro');
        $realAmount->addAmount($this->event->support_flat);
        foreach ($participants as $participationType => $participantData) {
            $expectedAmount->addAmount(new Amount($participantData['amount']['expected']['value'], 'Euro'));
            $realAmount->addAmount(new Amount($participantData['amount']['paid']['value'], 'Euro'));
        }

        return ['real' => [
            'amount' => $realAmount,
            'readable' => $realAmount->toString()
            ],
            'expected' => [
                'amount' => $expectedAmount,
                'readable' => $expectedAmount->toString(),
            ]
        ];
    }

    public function getParticipants(string $participationType) : array {
        $returnData = [];
        $returnData['amount'] = [
            'expected' => new Amount(0, 'Euro'),
            'paid' => new Amount(0, 'Euro')
        ];

        $returnData['count'] = 0;

        foreach ($this->event->participants()->where(
            [
                'participation_type' => $participationType,
                'unregistered_at' => null
            ]

        )->get() as $participant)
        {
            $returnData['count']++;
            $returnData['participants'][] = $participant;

            if ($participant->amount !== null) {
                $returnData['amount']['expected'] = $returnData['amount']['expected']->addAmount($participant->amount);
            }

            if ($participant->amount_paid !== null) {
                $returnData['amount']['paid'] = $returnData['amount']['paid']->addAmount($participant->amount_paid);
            }

        };

        $returnData['amount']['expected'] =
        [
            'value' => $returnData['amount']['expected']->getAmount(),
            'readable' => $returnData['amount']['expected']->toString()
        ];

        $returnData['amount']['paid'] =
        [
            'value' => $returnData['amount']['paid']->getAmount(),
            'readable' => $returnData['amount']['paid']->toString()
        ];

        return $returnData;
    }


    public function calculateAmount(
        string $participationType,
        string $feeType,
        DateTime $arrival,
        DateTime $departure,
        bool $hasSibling
    ) : Amount {
        $fee = collect([
            $this->event->participationFee1,
            $this->event->participationFee2,
            $this->event->participationFee3,
            $this->event->participationFee4,
        ])->filter(fn ($participationFee) => $participationFee !== null)
            ->first(fn ($participationFee) => $participationFee->type === $participationType);

        if ($fee === null) {
            return new Amount(0, 'Euro');
        }

        /** @var Amount $basicFee */
        $basicFee = $fee['amount_' . $feeType];
        $basicFee = $basicFee->multiply($this->getMultiplier());

        if ($this->event->pay_per_day) {
            $days = $arrival->diff($departure)->days + 1;
            $basicFee = $basicFee->multiply($days);
        }

        if ($hasSibling && $this->event->sibling_reduction) {
            $basicFee = $basicFee->multiply(0.5);
        }

        // Bei bestehender USt-Pflicht und Netto-Preismodus die USt aufschlagen; gespeichert wird immer der
        // finale Brutto-Betrag. Bei "inklusive" oder ohne Steuerpflicht bleibt der Beitrag unverändert.
        if ($this->event->tax_liable && $this->event->vat_pricing_mode === VatPricingMode::AddOn->value) {
            $basicFee = $basicFee->multiply(1 + $this->event->vat_rate / 100);
        }

        return new Amount(round($basicFee->getAmount(), 2), 'Euro');
    }

    /**
     * Löst die gewählten Zusätze (Event-Addons) gegen die Event-Definition auf und berechnet je Position den
     * Betrag: Pauschale => Preis, sonst Preis × Teilnahmetage. Bei USt-Pflicht + Netto-Preismodus (`add_on`)
     * wird die USt aufgeschlagen (analog Beitrag); bei „inclusive"/keiner Pflicht bleibt der Preis unverändert.
     * Kein Early-Bird-Aufschlag.
     *
     * @param array<int, string> $selectedKeys
     * @return array{items: array<int, array{key: string, title: string, price: float, flat: bool, days: int, amount: float}>, total: Amount}
     */
    public function resolveAddons(array $selectedKeys, DateTime $arrival, DateTime $departure): array
    {
        $days = $arrival->diff($departure)->days + 1;
        $addsVat = $this->event->tax_liable && $this->event->vat_pricing_mode === VatPricingMode::AddOn->value;

        $items = [];
        $total = new Amount(0, 'Euro');

        foreach ($this->event->addons ?? [] as $addon) {
            $key = $addon['key'] ?? null;
            if ($key === null || !in_array($key, $selectedKeys, true)) {
                continue;
            }

            $price = (float)($addon['price'] ?? 0);
            $flat = (bool)($addon['flat'] ?? false);
            $usedDays = $flat ? 1 : $days;

            $amount = $flat ? $price : $price * $days;
            if ($addsVat) {
                $amount *= 1 + $this->event->vat_rate / 100;
            }
            $amount = round($amount, 2);

            $items[] = [
                'key' => $key,
                'title' => $addon['title'] ?? $key,
                'price' => $price,
                'flat' => $flat,
                'days' => $usedDays,
                'amount' => $amount,
            ];

            $total->addAmount(new Amount($amount, 'Euro'));
        }

        return ['items' => $items, 'total' => $total];
    }
}


