<?php

namespace App\Http\Requests\Api;

use App\Enums\CustomerStatus;
use App\Enums\PermissionSlug;
use App\Http\Requests\Api\Concerns\ValidatesNominee;
use App\Models\Customer;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;

class UpdateCustomerRequest extends FormRequest
{
    use ValidatesNominee;

    public function authorize(): bool
    {
        return $this->user()?->hasPermission(PermissionSlug::CustomersUpdate) === true;
    }

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

        assert($customer instanceof Customer);

        $customer->loadMissing('nominee.documents');

        $complete = $this->customerStatus($customer)->requiresCompleteProfile();
        $nomineeRequired = ($complete && ! $customer->hasCompleteNominee())
            || $this->nomineeIsPresent();
        $requireDocuments = $this->customerNeedsNomineeDocuments($customer)
            || ($complete && ! $customer->hasCompleteNominee());

        return [
            'first_name' => ['sometimes', 'required', 'string', 'max:255'],
            'last_name' => ['sometimes', 'required', 'string', 'max:255'],
            'email' => [
                Rule::requiredIf($complete && blank($this->input('email', $customer->email))),
                'nullable',
                'email',
                'max:255',
                Rule::unique(Customer::class, 'email')->ignore($customer),
            ],
            'phone' => ['sometimes', 'required', 'string', 'max:30'],
            'cnic' => [
                Rule::requiredIf($complete && blank($this->input('cnic', $customer->cnic))),
                'nullable',
                'string',
                'max:20',
                Rule::unique(Customer::class, 'cnic')->ignore($customer),
            ],
            'status' => ['sometimes', 'required', 'string', Rule::in(CustomerStatus::values())],
            'address' => [
                Rule::requiredIf($complete && blank($this->input('address', $customer->address))),
                'nullable',
                'string',
                'max:1000',
            ],
            'agent_id' => ['sometimes', 'nullable', 'integer', Rule::exists('agents', 'id')],
            ...$this->nomineeRules($nomineeRequired, $requireDocuments),
        ];
    }

    /**
     * @return array{
     *     first_name?: string,
     *     last_name?: string,
     *     email?: string|null,
     *     phone?: string,
     *     cnic?: string|null,
     *     status?: CustomerStatus,
     *     address?: string|null,
     *     agent_id?: int|null,
     *     nominee?: array{
     *         full_name: string,
     *         cnic: string,
     *         phone: string|null,
     *         email: string|null
     *     }
     * }
     */
    public function payload(): array
    {
        /** @var array{
         *     first_name?: string,
         *     last_name?: string,
         *     email?: string|null,
         *     phone?: string,
         *     cnic?: string|null,
         *     status?: string,
         *     address?: string|null,
         *     agent_id?: int|null
         * } $data
         */
        $data = $this->safe()->only([
            'first_name',
            'last_name',
            'email',
            'phone',
            'cnic',
            'status',
            'address',
            'agent_id',
        ]);

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

        $nominee = $this->nomineePayload();

        if ($nominee !== null) {
            $data['nominee'] = $nominee;
        }

        return $data;
    }

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

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

        $this->prepareNomineeForValidation();
    }

    protected function customerStatus(Customer $customer): CustomerStatus
    {
        $status = $this->input('status');

        if (is_string($status) && $status !== '') {
            return CustomerStatus::tryFrom(strtoupper($status)) ?? $customer->status;
        }

        return $customer->status;
    }
}
