<?php

declare(strict_types=1);

namespace Maestro\Core\Modules\Offers\Http\Requests;

use Illuminate\Contracts\Validation\Validator;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
use Maestro\Core\Modules\Customers\Models\Contact;
use Maestro\Core\Modules\Offers\Enums\OfferDiscountType;
use Maestro\Core\Modules\Offers\Enums\OfferPricingType;
use Maestro\Core\Modules\Offers\Enums\OfferSectionRole;
use Maestro\Core\Modules\Offers\Enums\OfferStatus;
use Maestro\Core\Modules\Offers\Support\OfferDocumentBackgroundCatalog;

class StoreOfferRequest extends FormRequest
{
    public function authorize(): bool
    {
        return true;
    }

    /**
     * @return array<string, mixed>
     */
    public function rules(): array
    {
        return array_merge([
            'is_template' => ['sometimes', 'boolean'],
            'template_name' => ['nullable', 'string', 'max:255'],
            'subject' => ['required', 'string', 'max:255'],
            'customer_id' => ['nullable', 'exists:customers,id'],
            'contact_id' => ['nullable', 'exists:contacts,id'],
            'intro_html' => ['nullable', 'string'],
            'valid_until' => ['nullable', 'date'],
            'discount_type' => ['sometimes', 'string', Rule::enum(OfferDiscountType::class)],
            'discount_value' => ['sometimes', 'numeric', 'min:0'],
            'document_background_url' => [
                'nullable',
                'string',
                'max:2048',
                function (string $attribute, mixed $value, \Closure $fail): void {
                    if ($value === null || $value === '') {
                        return;
                    }
                    if (! is_string($value) || ! OfferDocumentBackgroundCatalog::isAllowed($value)) {
                        $fail('The selected document background is not allowed.');
                    }
                },
            ],
            'status' => ['sometimes', 'string', Rule::enum(OfferStatus::class)],
            'sections' => ['sometimes', 'array'],
            'sections.*.section_role' => ['sometimes', 'string', Rule::enum(OfferSectionRole::class)],
            'sections.*.children' => ['sometimes', 'array'],
            'sections.*.children.*.title' => ['required', 'string', 'max:255'],
            'sections.*.children.*.description_html' => ['nullable', 'string'],
            'sections.*.children.*.sort_order' => ['sometimes', 'integer', 'min:0'],
            'sections.*.children.*.section_role' => ['sometimes', 'string', Rule::enum(OfferSectionRole::class)],
            'sections.*.children.*.rows' => ['nullable', 'array'],
        ], $this->sectionRowRules(), $this->pricingCategoryRowRules());
    }

    /**
     * @return array<string, mixed>
     */
    protected function sectionRowRules(): array
    {
        return [
            'sections.*.title' => ['required', 'string', 'max:255'],
            'sections.*.description_html' => ['nullable', 'string'],
            'sections.*.sort_order' => ['sometimes', 'integer', 'min:0'],
            'sections.*.rows' => ['nullable', 'array'],
            'sections.*.rows.*.description_html' => ['nullable', 'string'],
            'sections.*.rows.*.pricing_type' => ['required', 'string', Rule::enum(OfferPricingType::class)],
            'sections.*.rows.*.custom_unit_label' => ['nullable', 'string', 'max:80'],
            'sections.*.rows.*.quantity' => ['sometimes', 'numeric'],
            'sections.*.rows.*.unit_price' => ['sometimes', 'numeric'],
            'sections.*.rows.*.discount_type' => ['sometimes', 'string', Rule::enum(OfferDiscountType::class)],
            'sections.*.rows.*.discount_value' => ['sometimes', 'numeric', 'min:0'],
            'sections.*.rows.*.vat_rate' => ['sometimes', 'numeric', 'min:0'],
            'sections.*.rows.*.sort_order' => ['sometimes', 'integer', 'min:0'],
            'sections.*.rows.*.offer_service_catalog_item_id' => ['nullable', 'exists:offer_service_catalog_items,id'],
        ];
    }

    /**
     * @return array<string, mixed>
     */
    protected function pricingCategoryRowRules(): array
    {
        return [
            'sections.*.children.*.rows.*.description_html' => ['nullable', 'string'],
            'sections.*.children.*.rows.*.pricing_type' => ['required_with:sections.*.children.*.rows', 'string', Rule::enum(OfferPricingType::class)],
            'sections.*.children.*.rows.*.custom_unit_label' => ['nullable', 'string', 'max:80'],
            'sections.*.children.*.rows.*.quantity' => ['sometimes', 'numeric'],
            'sections.*.children.*.rows.*.unit_price' => ['sometimes', 'numeric'],
            'sections.*.children.*.rows.*.discount_type' => ['sometimes', 'string', Rule::enum(OfferDiscountType::class)],
            'sections.*.children.*.rows.*.discount_value' => ['sometimes', 'numeric', 'min:0'],
            'sections.*.children.*.rows.*.vat_rate' => ['sometimes', 'numeric', 'min:0'],
            'sections.*.children.*.rows.*.sort_order' => ['sometimes', 'integer', 'min:0'],
            'sections.*.children.*.rows.*.offer_service_catalog_item_id' => ['nullable', 'exists:offer_service_catalog_items,id'],
        ];
    }

    public function withValidator(Validator $validator): void
    {
        $validator->after(function (Validator $v): void {
            $cust = $this->input('customer_id');
            $cont = $this->input('contact_id');
            if ($cust === null || $cont === null) {
                return;
            }
            $ok = Contact::query()
                ->whereKey((int) $cont)
                ->where('customer_id', (int) $cust)
                ->exists();
            if (! $ok) {
                $v->errors()->add('contact_id', 'Contact does not belong to the selected customer.');
            }
        });
    }
}
