<?php

namespace App\Http\Requests\Api;

use App\Enums\PaymentMethod;
use App\Enums\PaymentType;
use App\Enums\PermissionSlug;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\UploadedFile;
use Illuminate\Validation\Rule;

class ConfirmBookingPaymentRequest extends FormRequest
{
    public function authorize(): bool
    {
        return $this->user()?->hasPermission(PermissionSlug::BookingsUpdate) === true
            || $this->user()?->hasPermission(PermissionSlug::BookingsCreate) === true;
    }

    /**
     * @return array<string, ValidationRule|array<mixed>|string>
     */
    public function rules(): array
    {
        $method = $this->paymentMethod();

        return [
            'payment_type' => ['required', 'string', Rule::in(PaymentType::values())],
            'down_payment' => ['nullable', 'numeric', 'min:0'],
            'received_amount' => ['required', 'numeric', 'gt:0'],
            'payment_method' => ['required', 'string', Rule::in(PaymentMethod::values())],
            'payment_reference' => [
                Rule::requiredIf($method?->requiresReference() === true),
                'nullable',
                'string',
                'max:100',
            ],
            'account_name' => [
                Rule::requiredIf($method?->requiresAccountName() === true),
                'nullable',
                'string',
                'max:255',
            ],
            'acknowledge_cross_type' => ['sometimes', 'boolean'],
            'payment_proofs' => ['sometimes', 'array', 'max:2'],
            'payment_proofs.*' => ['file', 'image', 'mimes:jpeg,jpg,png,webp', 'max:5120'],
        ];
    }

    /**
     * @return array{
     *     payment_type: PaymentType,
     *     down_payment: string|null,
     *     received_amount: string,
     *     payment_method: PaymentMethod,
     *     payment_reference: string|null,
     *     account_name: string|null,
     *     acknowledge_cross_type: bool
     * }
     */
    public function payload(): array
    {
        /** @var array{
         *     payment_type: string,
         *     down_payment?: mixed,
         *     received_amount: mixed,
         *     payment_method: string,
         *     payment_reference?: string|null,
         *     account_name?: string|null,
         *     acknowledge_cross_type?: mixed
         * } $data
         */
        $data = $this->safe()->only([
            'payment_type',
            'down_payment',
            'received_amount',
            'payment_method',
            'payment_reference',
            'account_name',
            'acknowledge_cross_type',
        ]);

        $type = PaymentType::from($data['payment_type']);
        $received = (string) $data['received_amount'];
        $down = array_key_exists('down_payment', $data) && $data['down_payment'] !== null
            ? (string) $data['down_payment']
            : null;

        if ($type === PaymentType::DownPayment && $down === null) {
            $down = $received;
        }

        return [
            'payment_type' => $type,
            'down_payment' => $down,
            'received_amount' => $received,
            'payment_method' => PaymentMethod::from($data['payment_method']),
            'payment_reference' => $data['payment_reference'] ?? null,
            'account_name' => $data['account_name'] ?? null,
            'acknowledge_cross_type' => (bool) ($data['acknowledge_cross_type'] ?? false),
        ];
    }

    /**
     * @return list<UploadedFile>
     */
    public function proofs(): array
    {
        $files = $this->file('payment_proofs', []);

        if ($files instanceof UploadedFile) {
            return [$files];
        }

        return array_values(array_filter(
            $files,
            fn (mixed $file): bool => $file instanceof UploadedFile,
        ));
    }

    protected function prepareForValidation(): void
    {
        foreach (['payment_method', 'payment_type'] as $field) {
            if ($this->filled($field)) {
                $this->merge([
                    $field => strtoupper((string) $this->input($field)),
                ]);
            }
        }

        foreach (['payment_reference', 'account_name', 'down_payment'] as $field) {
            if ($this->exists($field) && ! $this->filled($field)) {
                $this->merge([$field => null]);
            }
        }
    }

    protected function paymentMethod(): ?PaymentMethod
    {
        $method = $this->input('payment_method');

        if (! is_string($method) || $method === '') {
            return null;
        }

        return PaymentMethod::tryFrom(strtoupper($method));
    }
}
