<?php

namespace App\Http\Requests\Documents;

use App\Enums\CodigoDescuento;
use App\Enums\CodigoImpuesto;
use App\Enums\CodigoTarifaIVA;
use App\Enums\CondicionVenta;
use App\Enums\MedioPago;
use App\Enums\Moneda;
use App\Enums\TipoDocumento;
use App\Enums\TipoIdentificacion;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
use Illuminate\Validation\Rules\Enum;

class AddDocRequest extends FormRequest
{
    protected $stopOnFirstFailure = true;

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

    protected function prepareForValidation(): void
    {
        $lineas = collect($this->input('lineas', []))
            ->values()
            ->map(function ($linea) {
                $montoTotal = ((float) ($linea['precio_unitario'] ?? 0)) * ((float) ($linea['cantidad'] ?? 0));
                $linea['ind_boniregalia'] = false;

                if (isset($linea['descuentos']) && is_array($linea['descuentos'])) {
                    foreach ($linea['descuentos'] as $index => $discount) {
                        $codigo = (string) ($discount['codigo_descuento'] ?? '');
                        if (in_array($codigo, [CodigoDescuento::REGALIA->value, CodigoDescuento::BONIFICACION->value], true)) {
                            $linea['descuentos'][$index]['monto_descuento'] = round($montoTotal, 5);
                            $linea['ind_boniregalia'] = true;
                        }
                    }
                }

                return $linea;
            });

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

    public function rules(): array
    {
        return [
            'tipo_documento' => ['required', Rule::enum(TipoDocumento::class)->only([
                TipoDocumento::FE,
                TipoDocumento::TE,
                TipoDocumento::NC,
                TipoDocumento::ND,
                TipoDocumento::FEC,
            ])],

            'cod_act_economica_emisor' => [Rule::requiredIf(fn () => $this->input('tipo_documento') !== TipoDocumento::FEC->value), 'nullable', 'string', 'size:6'],
            'cod_act_economica_receptor' => ['nullable', 'string', 'size:6'],

            'emisor' => ['required', 'array'],
            'emisor.nombre' => ['required', 'string', 'min:5', 'max:100'],
            'emisor.nombre_comercial' => ['nullable', 'string', 'min:3', 'max:80'],
            'emisor.tipo_identificacion' => ['required', new Enum(TipoIdentificacion::class)],
            'emisor.numero_identificacion' => ['required', 'string', 'max:20'],
            'emisor.ubicacion' => ['required', 'array'],
            'emisor.ubicacion.provincia' => ['required', 'regex:/^[1-7]$/'],
            'emisor.ubicacion.canton' => ['required', 'regex:/^[0-9]{2}$/'],
            'emisor.ubicacion.distrito' => ['required', 'regex:/^[0-9]{2}$/'],
            'emisor.ubicacion.sennas' => ['required', 'string', 'min:5', 'max:250'],
            'emisor.correo' => ['required', 'email', 'max:160'],

            'proveedor_compra' => [Rule::requiredIf(fn () => $this->input('tipo_documento') === TipoDocumento::FEC->value), 'array'],
            'proveedor_compra.nombre' => [Rule::requiredIf(fn () => $this->input('tipo_documento') === TipoDocumento::FEC->value), 'string', 'min:3', 'max:100'],
            'proveedor_compra.nombre_comercial' => ['nullable', 'string', 'min:3', 'max:80'],
            'proveedor_compra.tipo_identificacion' => [Rule::requiredIf(fn () => $this->input('tipo_documento') === TipoDocumento::FEC->value), new Enum(TipoIdentificacion::class)],
            'proveedor_compra.numero_identificacion' => [Rule::requiredIf(fn () => $this->input('tipo_documento') === TipoDocumento::FEC->value), 'string', 'max:20'],
            'proveedor_compra.correo' => ['nullable', 'email', 'max:160'],
            'proveedor_compra.cod_actividad' => ['nullable', 'string', 'size:6'],
            'proveedor_compra.otras_sennas_extranjero' => ['nullable', 'string', 'min:5', 'max:300'],
            'proveedor_compra.ubicacion' => ['nullable', 'array'],
            'proveedor_compra.ubicacion.provincia' => ['nullable', 'regex:/^[1-7]$/'],
            'proveedor_compra.ubicacion.canton' => ['nullable', 'regex:/^[0-9]{2}$/'],
            'proveedor_compra.ubicacion.distrito' => ['nullable', 'regex:/^[0-9]{2}$/'],
            'proveedor_compra.ubicacion.sennas' => ['nullable', 'string', 'min:5', 'max:250'],

            'receptor' => ['nullable', 'array'],
            'receptor.nombre' => ['nullable', 'string', 'min:3', 'max:100'],
            'receptor.nombre_comercial' => ['nullable', 'string', 'min:3', 'max:80'],
            'receptor.tipo_identificacion' => ['nullable', new Enum(TipoIdentificacion::class)],
            'receptor.numero_identificacion' => ['nullable', 'string', 'max:20'],
            'receptor.ubicacion' => ['nullable', 'array'],
            'receptor.ubicacion.provincia' => ['nullable', 'regex:/^[1-7]$/'],
            'receptor.ubicacion.canton' => ['nullable', 'regex:/^[0-9]{2}$/'],
            'receptor.ubicacion.distrito' => ['nullable', 'regex:/^[0-9]{2}$/'],
            'receptor.ubicacion.sennas' => ['nullable', 'string', 'min:5', 'max:160'],
            'receptor.telefono' => ['nullable', 'array'],
            'receptor.telefono.cod_pais' => ['nullable', 'digits_between:1,3'],
            'receptor.telefono.numero' => ['nullable', 'digits_between:8,20'],
            'receptor.correo' => ['nullable', 'email', 'max:160'],

            'moneda' => ['required', new Enum(Moneda::class)],
            'tipo_cambio' => ['nullable', 'numeric', 'min:0'],

            'condicion_venta' => ['required', new Enum(CondicionVenta::class)],
            'condicion_venta_otros' => ['nullable', 'string', 'max:100'],
            'plazo_credito' => ['nullable', 'integer', 'min:0', 'max:99999'],

            'lineas' => ['required', 'array', 'min:1'],
            'lineas.*.numero_linea' => ['nullable', 'integer', 'min:1'],
            'lineas.*.cantidad' => ['required', 'numeric', 'gt:0', 'regex:/^\d{1,13}(\.\d{1,3})?$/'],
            'lineas.*.detalle' => ['required', 'string', 'max:200'],
            'lineas.*.precio_unitario' => ['required', 'numeric', 'min:0'],
            'lineas.*.codigo_cabys' => ['required', 'regex:/^[0-9]{13}$/'],
            'lineas.*.unidad_medida' => ['nullable', 'string', 'max:15'],
            'lineas.*.unidad_medida_comercial' => ['nullable', 'string', 'max:20'],

            'lineas.*.descuentos' => ['nullable', 'array'],
            'lineas.*.descuentos.*' => ['array'],
            'lineas.*.descuentos.*.codigo_descuento' => ['required_with:lineas.*.descuentos.*', Rule::enum(CodigoDescuento::class)],
            'lineas.*.descuentos.*.codigo_descuento_otros' => ['nullable', 'string', 'max:100'],
            'lineas.*.descuentos.*.monto_descuento' => ['required_with:lineas.*.descuentos.*', 'numeric', 'min:0'],
            'lineas.*.descuentos.*.naturaleza_descuento' => ['nullable', 'string', 'max:80'],
            'lineas.*.ind_boniregalia' => ['sometimes', 'boolean'],

            'lineas.*.impuestos' => ['required', 'array', 'min:1'],
            'lineas.*.impuestos.*' => ['array'],
            'lineas.*.impuestos.*.codigo' => ['required_with:lineas.*.impuestos.*', Rule::enum(CodigoImpuesto::class)->only([
                CodigoImpuesto::IMPUESTO_AL_VALOR_AGREGADO,
                CodigoImpuesto::IVA_CALCULO_ESPECIAL,
                CodigoImpuesto::IMPUESTO_SELECTIVO_DE_CONSUMO,
                CodigoImpuesto::IMPUESTO_UNICO_A_LOS_COMBUSTIBLES,
                CodigoImpuesto::IMPUESTO_ESPECIFICO_BEBIDAS_ALCOHOLICAS,
                CodigoImpuesto::IMPUESTO_ESPECIFICO_BEBIDAS_ENVASADAS_SIN_ALCOHOL_Y_JABONES,
                CodigoImpuesto::IMPUESTO_A_LOS_PRODUCTOS_DE_TABACO,
                CodigoImpuesto::IMPUESTO_ESPECIFICO_AL_CEMENTO,
                CodigoImpuesto::OTROS,
            ])],
            'lineas.*.impuestos.*.codigo_impuesto_otros' => ['nullable', 'string', 'max:100'],
            'lineas.*.impuestos.*.codigo_tarifa_iva' => ['nullable', Rule::enum(CodigoTarifaIVA::class)],
            'lineas.*.impuestos.*.tarifa' => ['required_with:lineas.*.impuestos.*', 'numeric', 'min:0', 'max:99.99'],
            'lineas.*.impuestos.*.exoneracion' => ['nullable', 'array'],
            'lineas.*.impuestos.*.exoneracion.tipo_documento_ex' => ['required_with:lineas.*.impuestos.*.exoneracion', 'string', 'max:4'],
            'lineas.*.impuestos.*.exoneracion.tipo_documento' => ['nullable', 'string', 'max:4'],
            'lineas.*.impuestos.*.exoneracion.tipo_documento_ex_otros' => ['nullable', 'string', 'max:100'],
            'lineas.*.impuestos.*.exoneracion.numero_documento' => ['required_with:lineas.*.impuestos.*.exoneracion', 'string', 'max:30'],
            'lineas.*.impuestos.*.exoneracion.autorizacion' => ['nullable', 'string', 'max:30'],
            'lineas.*.impuestos.*.exoneracion.codigo_institucion' => ['nullable', 'string', 'max:4'],
            'lineas.*.impuestos.*.exoneracion.nombre_institucion' => ['nullable', 'string', 'max:180'],
            'lineas.*.impuestos.*.exoneracion.nombre_institucion_otros' => ['nullable', 'string', 'max:160'],
            'lineas.*.impuestos.*.exoneracion.fecha_emision' => ['required_with:lineas.*.impuestos.*.exoneracion', 'date'],
            'lineas.*.impuestos.*.exoneracion.fecha_vencimiento' => ['nullable', 'date'],
            'lineas.*.impuestos.*.exoneracion.tarifa_exonerada' => ['required_with:lineas.*.impuestos.*.exoneracion', 'numeric', 'min:0', 'max:100'],
            'lineas.*.impuestos.*.exoneracion.monto_exoneracion' => ['nullable', 'numeric', 'min:0'],
            'lineas.*.impuestos.*.exoneracion.articulo' => ['nullable', 'string', 'max:6'],
            'lineas.*.impuestos.*.exoneracion.inciso' => ['nullable', 'string', 'max:6'],
            'lineas.*.impuestos.*.exoneracion.cabys' => ['nullable', 'array'],
            'lineas.*.impuestos.*.exoneracion.cabys.*' => ['string', 'regex:/^[0-9]{13}$/'],
            'lineas.*.impuestos.*.exoneracion.vigente' => ['nullable', 'boolean'],

            'medio_pago' => ['required', 'array', 'min:1', 'max:4'],
            'medio_pago.*' => ['array'],
            'medio_pago.*.medio_pago_tipo' => ['required', 'distinct', new Enum(MedioPago::class)],
            'medio_pago.*.medio_pago_total' => ['nullable', 'numeric', 'min:0'],
            'medio_pago.*.medio_pago_otro' => ['nullable', 'string', 'max:100'],

            'info_referencia' => [Rule::requiredIf(fn () => in_array($this->input('tipo_documento'), [TipoDocumento::FEC->value, TipoDocumento::NC->value, TipoDocumento::ND->value], true)), 'array'],
            'info_referencia.tipo_doc' => ['required_with:info_referencia', 'string', 'size:2'],
            'info_referencia.tipo_doc_otro' => ['nullable', 'string', 'min:5', 'max:100'],
            'info_referencia.clave' => ['nullable', 'string', 'size:50'],
            'info_referencia.numero' => ['nullable', 'string', 'max:50'],
            'info_referencia.fecha_emision' => ['required_with:info_referencia'],
            'info_referencia.codigo' => ['required_with:info_referencia', 'string', 'size:2'],
            'info_referencia.codigo_otro' => ['nullable', 'string', 'min:5', 'max:100'],
            'info_referencia.razon' => ['required_with:info_referencia', 'string', 'max:180'],
        ];
    }

    public function withValidator($validator): void
    {
        $validator->after(function ($validator) {
            $tipoDocumento = (string) $this->input('tipo_documento');

            if ($tipoDocumento !== TipoDocumento::FEC->value) {
                return;
            }

            $proveedorTipo = (string) $this->input('proveedor_compra.tipo_identificacion');
            $condicionVenta = (string) $this->input('condicion_venta');
            $referenciaTipo = (string) $this->input('info_referencia.tipo_doc');
            $referenciaNumero = trim((string) ($this->input('info_referencia.numero') ?: $this->input('info_referencia.clave')));

            if ($referenciaNumero === '') {
                $validator->errors()->add('info_referencia.numero', 'La FEC requiere el consecutivo o número del documento de respaldo.');
            }

            if ($proveedorTipo === TipoIdentificacion::EXTRANJERO_NO_DOMICILIADO->value && trim((string) $this->input('proveedor_compra.otras_sennas_extranjero')) === '') {
                $validator->errors()->add('proveedor_compra.otras_sennas_extranjero', 'El proveedor extranjero no domiciliado requiere dirección extranjera.');
            }

            if ($proveedorTipo === TipoIdentificacion::NO_CONTRIBUYENTE->value && $condicionVenta !== CondicionVenta::VENTA_BIENES_USADOS_NO_CONTRIBUYENTE->value) {
                $validator->errors()->add('condicion_venta', 'Para proveedor no contribuyente en FEC use condición de venta 13.');
            }

            if ($referenciaTipo === '16' && $proveedorTipo !== TipoIdentificacion::EXTRANJERO_NO_DOMICILIADO->value) {
                $validator->errors()->add('info_referencia.tipo_doc', 'El tipo de referencia 16 debe usarse para proveedor extranjero no domiciliado.');
            }
        });
    }

    public function messages(): array
    {
        return [
            'lineas.*.cantidad.regex' => 'La cantidad debe usar hasta 13 enteros y 3 decimales.',
            'lineas.*.codigo_cabys.regex' => 'El código CAByS debe usar 13 dígitos.',
            'emisor.ubicacion.provincia.regex' => 'La provincia del emisor no es válida.',
            'receptor.ubicacion.provincia.regex' => 'La provincia del receptor no es válida.',
        ];
    }
}
