<?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 StoreAgentRequest extends FormRequest
{
    public function authorize(): bool
    {
        return $this->user()?->hasPermission(PermissionSlug::AgentsCreate) === true;
    }

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

        return [
            'name' => ['required', 'string', 'max:255'],
            'company_name' => ['nullable', 'string', 'max:255'],
            'phone' => ['required', 'string', 'max:30'],
            'email' => ['nullable', 'email', 'max:255', Rule::unique(Agent::class, 'email')],
            'address' => ['nullable', 'string', 'max:1000'],
            'commission_type' => [
                'nullable',
                'required_with:commission_value',
                'string',
                Rule::in(CommissionType::values()),
            ],
            'commission_value' => [
                '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',
        ]);

        return [
            'name' => $data['name'],
            'company_name' => $data['company_name'] ?? null,
            'phone' => $data['phone'],
            'email' => $data['email'] ?? null,
            'address' => $data['address'] ?? null,
            'commission_type' => isset($data['commission_type']) && is_string($data['commission_type'])
                ? CommissionType::from($data['commission_type'])
                : null,
            'commission_value' => isset($data['commission_value']) && $data['commission_value'] !== null
                ? (string) $data['commission_value']
                : null,
            'status' => isset($data['status'])
                ? AgentStatus::from($data['status'])
                : AgentStatus::Active,
        ];
    }

    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(): ?CommissionType
    {
        $type = $this->input('commission_type');

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

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