<?php

namespace App\Http\Requests\Api;

use App\Enums\AgentStatus;
use App\Enums\CommissionType;
use App\Enums\PermissionSlug;
use App\Models\Agent;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;

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

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

        assert($agent instanceof Agent);

        $percentage = $this->commissionType($agent) === CommissionType::Percentage;

        return [
            'name' => ['sometimes', 'required', 'string', 'max:255'],
            'company_name' => ['sometimes', 'nullable', 'string', 'max:255'],
            'phone' => ['sometimes', 'required', 'string', 'max:30'],
            'email' => [
                'sometimes',
                'nullable',
                'email',
                'max:255',
                Rule::unique(Agent::class, 'email')->ignore($agent),
            ],
            'address' => ['sometimes', 'nullable', 'string', 'max:1000'],
            'commission_type' => [
                'sometimes',
                'nullable',
                'required_with:commission_value',
                'string',
                Rule::in(CommissionType::values()),
            ],
            'commission_value' => [
                'sometimes',
                'nullable',
                'required_with:commission_type',
                'numeric',
                'min:0',
                Rule::when($percentage, ['max:100']),
            ],
            'status' => ['sometimes', 'required', 'string', Rule::in(AgentStatus::values())],
        ];
    }

    /**
     * @return array{
     *     name?: string,
     *     company_name?: string|null,
     *     phone?: string,
     *     email?: string|null,
     *     address?: string|null,
     *     commission_type?: CommissionType|null,
     *     commission_value?: string|null,
     *     status?: AgentStatus
     * }
     */
    public function payload(): array
    {
        /** @var array{
         *     name?: string,
         *     company_name?: string|null,
         *     phone?: string,
         *     email?: string|null,
         *     address?: string|null,
         *     commission_type?: string|null,
         *     commission_value?: mixed,
         *     status?: string
         * } $data
         */
        $data = $this->safe()->only([
            'name',
            'company_name',
            'phone',
            'email',
            'address',
            'commission_type',
            'commission_value',
            'status',
        ]);

        if (array_key_exists('commission_type', $data)) {
            $data['commission_type'] = is_string($data['commission_type'])
                ? CommissionType::from($data['commission_type'])
                : null;
        }

        if (array_key_exists('commission_value', $data)) {
            $data['commission_value'] = $data['commission_value'] === null
                ? null
                : (string) $data['commission_value'];
        }

        if (isset($data['status'])) {
            $data['status'] = AgentStatus::from($data['status']);
        }

        return $data;
    }

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

        foreach (['company_name', 'email', 'address', 'commission_type', 'commission_value'] as $field) {
            if ($this->exists($field) && ! $this->filled($field)) {
                $this->merge([$field => null]);
            }
        }
    }

    protected function commissionType(Agent $agent): ?CommissionType
    {
        $type = $this->input('commission_type', $agent->commission_type?->value);

        if ($type instanceof CommissionType) {
            return $type;
        }

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

        return CommissionType::tryFrom(strtoupper($type));
    }
}
