<?php

namespace App\Http\Requests;

use App\Enums\ApprovalStatusEnum;
use App\Enums\GenderEnum;
use App\Enums\UserTypeEnum;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;

class ProviderRequest extends FormRequest
{
    public function rules(): array
    {
        $providerId = $this->route('id');

        $dailyPriceMin = config('provider.daily_price_min');

        $rules = [
            'document' => [
                'sometimes',
                'string',
                'regex:/^[0-9]{11}$|^[0-9]{14}$/',
                function ($attribute, $value, $fail) use ($providerId) {
                    if (!$this->isValidCpfCnpj($value)) {
                        $fail(__('validation.custom.document.invalid'));
                    }
                },
                Rule::unique('providers', 'document')
                    ->ignore($providerId)
                    ->whereNull('deleted_at'),
            ],

            'rg' => 'sometimes|nullable|string|max:20',

            'user_id' => [
                'sometimes',
                'exists:users,id',
                Rule::unique('providers', 'user_id')
                    ->ignore($providerId)
                    ->whereNull('deleted_at'),
                function ($attribute, $value, $fail) use ($providerId) {
                    $clientExists = \DB::table('clients')
                        ->where('user_id', $value)
                        ->whereNull('deleted_at')
                        ->exists();
                    
                    if ($clientExists) {
                        $fail(__('validation.custom.user_id.already_linked_to_client'));
                    }

                    $providerExists = \DB::table('providers')
                        ->where('user_id', $value)
                        ->whereNull('deleted_at')
                        ->when($providerId, function ($query) use ($providerId) {
                            $query->where('id', '!=', $providerId);
                        })
                        ->exists();

                    if ($providerExists) {
                        $fail(__('validation.custom.user_id.already_linked_to_provider'));
                    }
                },
            ],

            'average_rating' => 'sometimes|nullable|numeric|min:0|max:5',
            'total_services' => 'sometimes|integer|min:0',
            'birth_date'     => 'sometimes|nullable|date|before:today',
            'gender'         => ['sometimes', 'nullable', Rule::enum(GenderEnum::class)],

            'selfie_verified' => [
                'sometimes',
                'boolean',
                Rule::prohibitedIf(fn () => ! $this->user()?->canModerateProviders()),
            ],

            'document_verified'      => 'sometimes|boolean',
            'approval_status'        => ['sometimes', Rule::enum(ApprovalStatusEnum::class)],
            'daily_price_8h'         => "sometimes|nullable|numeric|min:{$dailyPriceMin}|max:500",
            'daily_price_6h'         => 'sometimes|nullable|numeric',
            'daily_price_4h'         => 'sometimes|nullable|numeric',
            'daily_price_2h'         => 'sometimes|nullable|numeric',
            'profile_media_id'       => 'sometimes|nullable|exists:media,id',
            'avatar'                 => 'sometimes|file|image|mimes:jpg,jpeg,png,webp|max:5120',
            'recipient_name'         => 'sometimes|string|max:255',
            'recipient_email'        => 'sometimes|email|max:255',
            'recipient_description'  => 'sometimes|nullable|string',
            'recipient_document'     => 'sometimes|string|max:20',
            'recipient_type'         => ['sometimes', Rule::in(['individual', 'company'])],
            'recipient_payment_mode' => ['sometimes', Rule::in(['bank_transfer'])],
            'recipient_metadata'     => 'sometimes|array',
        ];

        if ($this->isMethod('post')) {
            $rules['document'] = [
                'required',
                'string',
                'regex:/^[0-9]{11}$|^[0-9]{14}$/',
                function ($attribute, $value, $fail) {
                    if (!$this->isValidCpfCnpj($value)) {
                        $fail(__('validation.custom.document.invalid'));
                    }
                },
                Rule::unique('providers', 'document')->whereNull('deleted_at'),
            ];

            $rules['recipient_name']                                     = 'required|string|max:255';
            $rules['recipient_email']                                    = 'required|email|max:255';
            $rules['recipient_description']                              = 'required|string';
            $rules['recipient_document']                                 = 'required|string|max:20';
            $rules['recipient_type']                                     = ['required', Rule::in(['individual', 'company'])];
            $rules['recipient_payment_mode']                             = ['required', Rule::in(['bank_transfer'])];
            $rules['recipient_default_bank_account']                     = 'required|array';
            $rules['recipient_default_bank_account.holder_name']         = 'required|string|max:255';
            $rules['recipient_default_bank_account.holder_type']         = ['required', Rule::in(['individual', 'company'])];
            $rules['recipient_default_bank_account.holder_document']     = 'required|string|max:20';
            $rules['recipient_default_bank_account.bank']                = 'required|string|max:3';
            $rules['recipient_default_bank_account.branch_number']       = 'required|string|max:4';
            $rules['recipient_default_bank_account.branch_check_digit']  = 'sometimes|nullable|string|max:2';
            $rules['recipient_default_bank_account.account_number']      = 'required|string|max:13';
            $rules['recipient_default_bank_account.account_check_digit'] = 'required|string|max:2';
            $rules['recipient_default_bank_account.type']                = ['required', Rule::in(['checking', 'savings'])];
            $rules['recipient_default_bank_account.metadata']            = 'required|array';
            $rules['recipient_metadata']                                 = 'required|array';

            $rules['user_id'] = [
                'required',
                'exists:users,id',
                Rule::unique('providers', 'user_id')->whereNull('deleted_at'),
                function ($attribute, $value, $fail) {
                    $clientExists = \DB::table('clients')
                        ->where('user_id', $value)
                        ->whereNull('deleted_at')
                        ->exists();
                    
                    if ($clientExists) {
                        $fail(__('validation.custom.user_id.already_linked_to_client'));
                    }

                    $providerExists = \DB::table('providers')
                        ->where('user_id', $value)
                        ->whereNull('deleted_at')
                        ->exists();

                    if ($providerExists) {
                        $fail(__('validation.custom.user_id.already_linked_to_provider'));
                    }
                },
            ];
        }

        return $rules;
    }

    /**
     * Valida CPF ou CNPJ
     */
    private function isValidCpfCnpj(string $value): bool
    {
        $value = preg_replace('/[^0-9]/', '', $value);

        if (strlen($value) === 11) {
            return $this->isValidCpf($value);
        } elseif (strlen($value) === 14) {
            return $this->isValidCnpj($value);
        }

        return false;
    }

    /**
     * Valida CPF
     */
    private function isValidCpf(string $cpf): bool
    {
        // Elimina CPFs inválidos conhecidos
        if (preg_match('/(\d)\1{10}/', $cpf)) {
            return false;
        }

        // Valida primeiro dígito verificador
        for ($t = 9; $t < 11; $t++) {
            for ($d = 0, $c = 0; $c < $t; $c++) {
                $d += $cpf[$c] * (($t + 1) - $c);
            }

            $d = ((10 * $d) % 11) % 10;

            if ($cpf[$c] != $d) {
                return false;
            }
        }

        return true;
    }

    /**
     * Valida CNPJ
     */
    private function isValidCnpj(string $cnpj): bool
    {
        // Elimina CNPJs inválidos conhecidos
        if (preg_match('/(\d)\1{13}/', $cnpj)) {
            return false;
        }

        // Valida primeiro dígito verificador
        $length  = strlen($cnpj) - 2;
        $numbers = substr($cnpj, 0, $length);
        $digits  = substr($cnpj, $length);
        $sum     = 0;
        $pos     = $length - 7;

        for ($i = $length; $i >= 1; $i--) {
            $sum += $numbers[$length - $i] * $pos--;

            if ($pos < 2) {
                $pos = 9;
            }
        }

        $result = $sum % 11 < 2 ? 0 : 11 - $sum % 11;

        if ($result != $digits[0]) {
            return false;
        }

        // Valida segundo dígito verificador

        $length  = $length + 1;
        $numbers = substr($cnpj, 0, $length);
        $sum     = 0;
        $pos     = $length - 7;

        for ($i = $length; $i >= 1; $i--) {
            $sum += $numbers[$length - $i] * $pos--;

            if ($pos < 2) {
                $pos = 9;
            }
        }

        $result = $sum % 11 < 2 ? 0 : 11 - $sum % 11;

        if ($result != $digits[1]) {
            return false;
        }

        return true;
    }
}
