<?php

namespace App\Http\Requests\Concerns;

use App\Models\PermanentEmployeeAdvance;
use App\Support\StaffAdvanceQuantityMode;
use App\Support\StaffAdvanceStock;
use App\Support\StaffAdvanceType;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;
use Illuminate\Validation\Validator;

trait ValidatesPermanentEmployeeAdvanceInput
{
    /**
     * @return array<string, array<int, mixed>>
     */
    protected function advanceRules(bool $requireEmployee = true): array
    {
        $rules = [
            'type' => ['required', Rule::in(StaffAdvanceType::values())],
            'advance_date' => ['required', 'date'],
            'notes' => ['nullable', 'string', 'max:2000'],
            'amount' => ['nullable', 'numeric', 'min:0.01'],
            'factory_product_id' => ['nullable', 'integer', 'exists:factory_products,id'],
            'quantity_mode' => ['nullable', 'string', Rule::in(StaffAdvanceQuantityMode::values())],
            'quantity' => ['nullable', 'numeric', 'min:0.001'],
            'total_weight' => ['nullable', 'numeric', 'min:0.001'],
            'weight_price' => ['nullable', 'numeric', 'min:0'],
        ];

        if ($requireEmployee) {
            $rules['permanent_employee_id'] = ['required', 'integer', 'exists:permanent_employees,id'];
        }

        return $rules;
    }

    public function withValidator(Validator $validator): void
    {
        $validator->after(function (Validator $validator): void {
            $type = $this->input('type');

            if ($type === StaffAdvanceType::CASH) {
                $amount = $this->input('amount');
                if ($amount === null || $amount === '' || (float) $amount <= 0) {
                    $validator->errors()->add('amount', __('validation.required', ['attribute' => 'amount']));
                }

                return;
            }

            if ($type !== StaffAdvanceType::PRODUCT) {
                return;
            }

            $quantityMode = StaffAdvanceQuantityMode::normalize($this->input('quantity_mode'));

            foreach (['factory_product_id', 'quantity', 'total_weight', 'weight_price'] as $field) {
                $value = $this->input($field);
                if ($value === null || $value === '') {
                    $validator->errors()->add($field, __('validation.required', ['attribute' => $field]));
                }
            }

            if ($this->input('quantity_mode') === null || $this->input('quantity_mode') === '') {
                $validator->errors()->add('quantity_mode', __('validation.required', ['attribute' => 'quantity_mode']));
            }

            $quantity = (float) $this->input('quantity');
            $totalWeight = (float) $this->input('total_weight');
            $unitPrice = (float) $this->input('weight_price');

            if ($quantity > 0 && $totalWeight > 0 && $unitPrice >= 0) {
                $this->merge([
                    'quantity_mode' => $quantityMode,
                    'amount' => PermanentEmployeeAdvance::calculateProductAmount(
                        $quantityMode,
                        $quantity,
                        $totalWeight,
                        $unitPrice,
                    ),
                ]);
            }

            if ($validator->errors()->isNotEmpty()) {
                return;
            }

            $creditByProduct = [];
            $current = $this->route('advance');

            if ($current instanceof PermanentEmployeeAdvance && $current->isProduct() && $current->factory_product_id) {
                $creditByProduct[(int) $current->factory_product_id] = StaffAdvanceStock::packQuantity($current->quantity);
            }

            try {
                StaffAdvanceStock::assertSufficientStock([[
                    'factory_product_id' => $this->input('factory_product_id'),
                    'quantity' => $this->input('quantity'),
                ]], $creditByProduct);
            } catch (ValidationException $exception) {
                foreach ($exception->errors() as $field => $messages) {
                    foreach ($messages as $message) {
                        $validator->errors()->add($field, $message);
                    }
                }
            }
        });
    }

    /**
     * @return array<string, mixed>
     */
    public function validatedAdvanceAttributes(): array
    {
        $validated = $this->validated();
        $type = $validated['type'];

        $attributes = [
            'permanent_employee_id' => $validated['permanent_employee_id'] ?? null,
            'type' => $type,
            'advance_date' => $validated['advance_date'],
            'notes' => $validated['notes'] ?? null,
            'is_active' => true,
        ];

        if ($type === StaffAdvanceType::CASH) {
            $attributes['amount'] = round((float) $validated['amount'], 2);
            $attributes['factory_product_id'] = null;
            $attributes['quantity_mode'] = null;
            $attributes['quantity'] = null;
            $attributes['total_weight'] = null;
            $attributes['weight_price'] = null;

            return $attributes;
        }

        $quantityMode = StaffAdvanceQuantityMode::normalize($validated['quantity_mode'] ?? null);
        $quantity = round((float) $validated['quantity'], 3);
        $totalWeight = round((float) $validated['total_weight'], 3);
        $unitPrice = round((float) $validated['weight_price'], 2);

        $attributes['amount'] = PermanentEmployeeAdvance::calculateProductAmount(
            $quantityMode,
            $quantity,
            $totalWeight,
            $unitPrice,
        );
        $attributes['factory_product_id'] = (int) $validated['factory_product_id'];
        $attributes['quantity_mode'] = $quantityMode;
        $attributes['quantity'] = $quantity;
        $attributes['total_weight'] = $totalWeight;
        $attributes['weight_price'] = $unitPrice;

        return $attributes;
    }
}
