<?php 

namespace App\Http\Requests\Documents;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rules\Enum;
use Illuminate\Validation\Rule;

use Closure;

use App\Enums\TipoIdentificacion;
use App\Enums\CodigoTarifaIVA;
use App\Enums\CodigoImpuesto;
use App\Enums\CondicionVenta;
use App\Enums\CodigoDescuento;
use App\Enums\Moneda;
use App\Enums\MedioPago;
use App\Enums\TipoDocumento;

use Illuminate\Contracts\Validation\Validator;
use Illuminate\Http\Exceptions\HttpResponseException;

class AddDocRequest extends FormRequest
{
    protected $stopOnFirstFailure = true;

    public function authorize(): bool
    {
        return true;
    }

    protected function prepareForValidation()
    {
        $lineas = collect($this->input('lineas', []))
            ->values()
            ->map(function ($linea, $index) 
            {
                $montoTotal = $linea["precio_unitario"] * $linea["cantidad"];
                $linea["ind_boniregalia"]   = false;
                
                if (isset($linea['descuentos']) && \count($linea['descuentos']) > 0 && \is_array($linea['descuentos'])) 
                {
                    $code = $linea['descuentos'][0] ?? null;
                
                    if (
                        $code !== null && 
                        isset($code['codigo_descuento']) 
                        && (
                            $code['codigo_descuento'] === CodigoDescuento::REGALIA->value 
                            || $code['codigo_descuento'] === CodigoDescuento::BONIFICACION->value
                        )
                    ) 
                    {
                        $linea['descuentos'][0]["monto_descuento"] = round($montoTotal, 5);
                        $linea["ind_boniregalia"] = true;
                    }
                }

                return $linea;
            });

        $this->merge(['lineas' => $lineas->toArray()]);
    }


    public function rules(): array
    {
        return 
        [
            // TIPO DOCUMENTO
            "tipo_documento" => "required|", Rule::enum(TipoDocumento::class)->only([
                TipoDocumento::FE,
                TipoDocumento::TE,
                TipoDocumento::NC,
                TipoDocumento::ND
            ]),

            "cod_act_economica_emisor" => "required|string|min:6|max:6",
            "cod_act_economica_receptor" => "exclude_if:cod_act_economica_receptor,null|string|min:6|max:6",

            // EMISOR
            "emisor"                            => "required|array|required_array_keys:nombre,tipo_identificacion,numero_identificacion",
            "emisor.nombre"                     => "string|min:5|max:100",
            "emisor.nombre_comercial"           => "nullable|string|min:3|max:80",
            
            "emisor.tipo_identificacion"        => new Enum(TipoIdentificacion::class),
            "emisor.numero_identificacion"      => "numeric|min_digits:9|max_digits:12",

            "emisor.ubicacion"                  => "required|array|required_array_keys:provincia,canton,distrito,sennas",
            "emisor.ubicacion.provincia"        => "numeric|min_digits:1|max_digits:1",
            "emisor.ubicacion.canton"           => "numeric|min_digits:2|max_digits:2",
            "emisor.ubicacion.distrito"         => "numeric|min_digits:2|max_digits:2",
            "emisor.ubicacion.sennas"           => "string|min:5|max:160",

            "emisor.correo"                     => "required|email",

            // RECEPTOR
            "receptor"                      => "required_if:tipo_documento,01|exclude_if:receptor,null|exclude_if:tipo_documento,04|array|required_array_keys:nombre,tipo_identificacion,numero_identificacion",
            "receptor.nombre"               => "string|min:5|max:100",
            "receptor.nombre_comercial"     => "exclude_if:receptor.nombre_comercial,null",
            
            "receptor.tipo_identificacion"        => new Enum(TipoIdentificacion::class),
            "receptor.numero_identificacion"      => "numeric|min_digits:9|max_digits:12",

            "receptor.ubicacion"            => "nullable|array|required_array_keys:provincia,canton,distrito,sennas",
            "receptor.ubicacion.provincia"  => "numeric|min_digits:1|max_digits:1",
            "receptor.ubicacion.canton"     => "numeric|min_digits:2|max_digits:2",
            "receptor.ubicacion.distrito"   => "numeric|min_digits:2|max_digits:2",
            "receptor.ubicacion.sennas"     => "string|min:5|max:160",

            "receptor.telefono"             => "exclude_if:receptor.telefono,null|array|required_array_keys:cod_pais,numero",
            "receptor.telefono.cod_pais"    => "numeric|min_digits:1|max_digits:3",
            "receptor.telefono.numero"      => "numeric|min_digits:8|max_digits:20",

            "receptor.correo"               => "email",

            // MONEDA
            "moneda"        => "required|", new Enum(Moneda::class),
            "tipo_cambio" => "exclude_if:tipo_cambio,null|nullable|numeric",
        
            // CONDICION VENTA
            "condicion_venta"       => "required|", Rule::enum(CondicionVenta::class)->only([
                CondicionVenta::CONTADO,
                CondicionVenta::OTROS
            ]),
            "condicion_venta_otros" => [
                "required_if:condicion_venta," . CondicionVenta::OTROS->value,
                "exclude_unless:condicion_venta," . CondicionVenta::OTROS->value,
                "string",
            ],

            // LINEAS
            "lineas"                    => "required|array|min:1",
            "lineas.*.numero_linea"     => "numeric",
            "lineas.*.cantidad"         => "required|integer",
            "lineas.*.detalle"          => "required|string|max:200",
            "lineas.*.precio_unitario"  => "required|numeric|min:1",
            "lineas.*.codigo_cabys"     => "required|string|min:13",
            // "lineas.*.monto_total"      => "required|decimal:0,5",

            "lineas.*.unidad_medida"               => "nullable|string|max:15",
            "lineas.*.unidad_medida_comercial"     => "nullable|string|max:20",


            // DESCUENTO
            "lineas.*.descuentos"   => "exclude_if:lineas.*.descuentos,null|nullable|array",
            "lineas.*.descuentos.*" => "array|required_array_keys:codigo_descuento,monto_descuento,naturaleza_descuento",
            "lineas.*.descuentos.*.codigo_descuento"        => Rule::enum(CodigoDescuento::class),
            "lineas.*.descuentos.*.codigo_descuento_otros"  => [
                "required_if:lineas.*.descuentos.*.codigo_descuento," . CodigoDescuento::OTROS->value, 
                "exclude_unless:lineas.*.descuentos.*.codigo_descuento," . CodigoDescuento::OTROS->value, 
                "string",
            ],
            "lineas.*.descuentos.*.monto_descuento"         => ["required","decimal:0,5"],
            "lineas.*.descuentos.*.naturaleza_descuento"    => "nullable|max:80|required_if:lineas.*.descuentos.*.codigo_descuento," . CodigoDescuento::OTROS->value, "|string",

            "lineas.*.ind_boniregalia"  => "sometimes|boolean",

            "lineas.*.impuestos"    => "required|array",
            "lineas.*.impuestos.*"  => "array|required_array_keys:codigo,tarifa",
            
            "lineas.*.impuestos.*.codigo"                   => "required|", Rule::enum(CodigoImpuesto::class)->only([
                CodigoImpuesto::IMPUESTO_AL_VALOR_AGREGADO,
                CodigoImpuesto::OTROS,
                CodigoImpuesto::IVA_CALCULO_ESPECIAL
            ]),
            "lineas.*.impuestos.*.codigo_impuesto_otros"    => "exclude_unless:lineas.*.impuestos.*.codigo," . CodigoImpuesto::OTROS->value . "|required_if:lineas.*.impuestos.*.codigo," . CodigoImpuesto::OTROS->value, "|string|min:5|max:100",
            "lineas.*.impuestos.*.codigo_tarifa_iva"        => [
                Rule::enum(CodigoTarifaIVA::class),
                "required_if:lineas.*.impuestos.*.codigo," . CodigoImpuesto::IMPUESTO_AL_VALOR_AGREGADO->value,
                "required_if:lineas.*.impuestos.*.codigo," . CodigoImpuesto::IVA_CALCULO_ESPECIAL->value, 
            ],
            "lineas.*.impuestos.*.tarifa"                   => "required|numeric|min:1",
            
            // MEDIOS DE PAGO
            "medio_pago"                    => "required|array|min:1|max:4",
            "medio_pago.*"                  => "array|required_array_keys:medio_pago_tipo",
            "medio_pago.*.medio_pago_tipo"  => "required|distinct|", new Enum(MedioPago::class),
            "medio_pago.*.medio_pago_total" => Rule::when(\count($this->input("medio_pago", [])) >= 2, ["required", "decimal:2"]),
            "medio_pago.*.medio_pago_otro"  => "nullable|string|required_if:medio_pago.*.medio_pago_tipo," . MedioPago::OTROS->value,
        
            "info_referencia" => [
                "required_if:tipo_documento," . TipoDocumento::ND->value,
                "required_if:tipo_documento," . TipoDocumento::NC->value,
                "exclude_if:tipo_documento," . TipoDocumento::TE->value,
                "exclude_if:tipo_documento," . TipoDocumento::FE->value,
                "array"
            ],
            "info_referencia.tipo_doc" => "required_with:info_referencia",
            "info_referencia.clave" => "required_with:info_referencia|string|min:50|max:50",
            "info_referencia.fecha_emision" => "required_with:info_referencia",
            "info_referencia.codigo" => "required_with:info_referencia",
            "info_referencia.razon" => "required_with:info_referencia",
        ];
    }

    public function messages(): array
    {
        return [
            'lineas.*.cantidad' => 'Debe indicar la cantidad de la linea de detalle #:position',
            'medio_pago.*.medio_pago_otro.required_if' => 'El campo medio_pago_otro es obligatorio cuando medio_pago_tipo es [99]',
            'lineas.*.descuentos.*.codigo_descuento_otros.required' => 'El campo codigo_descuento_otro es obligatorio cuando codigo_descuento es [99]'
        ];
    }

    public function failedValidation(Validator $validator)
    {
        $errors = collect($validator->errors()->toArray())
            ->map(fn ($messages) => $messages[0])
            ->toArray();

        throw new HttpResponseException(response()->json($errors, 422));
    }
}