<?php

namespace App\Http\Requests;

use App\Enums\CSVDelimiter;
use App\Enums\ExportColumnJsonType;
use App\Enums\MappingOperator;
use App\Exports\JsonMapExport;
use Illuminate\Validation\Rule;
use Illuminate\Validation\Validator;

class UpdateExportMapTemplate extends ExportMapTemplateRequest
{
    /**
     * Get the validation rules that apply to the request.
     *
     * @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
     */
    #[\Override]
    public function rules(): array
    {
        return [
            'is_published' => ['required', 'boolean'],
            'sales_channel' => ['nullable', 'string', 'max:100'],
            'title' => ['required', 'string', 'max:255'],
            'type' => ['sometimes', 'string', Rule::in(array_keys(config('export_maps.types', [])))],
            'columns' => ['required', 'array', 'min:1'],
            'columns.*' => ['required', 'string'],
            'headers' => ['nullable', 'array'],
            'headers.*' => ['nullable', 'string'],
            'mapping_rules' => ['nullable', 'array'],
            'mapping_rules.*.when' => ['required', 'array'],
            'mapping_rules.*.when.field' => ['required', 'string'],
            'mapping_rules.*.when.op' => ['required', 'string', Rule::in(MappingOperator::values())],
            'mapping_rules.*.when.value' => ['nullable'],
            'mapping_rules.*.then' => ['required', 'array', 'min:1'],
            'mapping_rules.*.then.*.action' => ['required', 'string', Rule::in(['set', 'append', 'prepend', 'merge'])],
            'mapping_rules.*.then.*.target' => ['required', 'string'],
            'mapping_rules.*.then.*.value' => ['nullable'],
            'mapping_rules.*.then.*.source' => ['nullable', 'string'],
            'mapping_rules.*.then.*.separator' => ['nullable', 'string'],
            'format' => ['nullable', 'string', Rule::in(config('export_maps.export_formats', []))],
            'config' => ['nullable', 'array'],
            'config.delimiter' => ['nullable', 'string', Rule::in(CSVDelimiter::values())],
            'config.json_envelope' => ['nullable', 'string', 'json', 'max:8192'],
            'config.column_types' => ['nullable', 'array'],
            'config.column_types.*' => ['nullable', 'string', Rule::in(ExportColumnJsonType::values())],
            'config.column_paths' => ['nullable', 'array'],
            'config.column_paths.*' => ['nullable', 'string', 'max:2048'],
        ];
    }

    public function withValidator(Validator $validator): void
    {
        $validator->after(function ($validator) {
            foreach (($this->input('mapping_rules', []) ?? []) as $i => $rule) {
                $op = MappingOperator::fromString($rule['when']['op'] ?? null);
                if (!$op) {
                    continue;
                }

                $val = $rule['when']['value'] ?? null;

                // value presence
                if ($op->expectsValue() && ($val === null || (is_string($val) && trim($val) === ''))) {
                    $validator->errors()
                        ->add("mapping_rules.$i.when.value", 'This operator requires a value.');
                }

                // numeric for GT/GTE/LT/LTE
                if ($op->isNumeric() && !is_numeric($val)) {
                    $validator->errors()
                        ->add("mapping_rules.$i.when.value", 'Value must be numeric for this operator.');
                }

                // merge actions require a source field path to read from
                foreach (($rule['then'] ?? []) as $j => $action) {
                    if (($action['action'] ?? null) === 'merge' && empty($action['source'])) {
                        $validator->errors()
                            ->add("mapping_rules.$i.then.$j.source", 'The merge action requires a source field.');
                    }
                }
            }

            $envelope = $this->input('config.json_envelope');
            if (is_string($envelope) && trim($envelope) !== ''
                && !str_contains($envelope, JsonMapExport::TOKEN_RESULTS)
            ) {
                $validator->errors()->add(
                    'config.json_envelope',
                    'JSON envelope must contain "' . JsonMapExport::TOKEN_RESULTS . '" where rows should be inserted.'
                );
            }
        });
    }

    #[\Override]
    public function messages(): array
    {
        return [
            'columns.required' => 'At least one column is required.',
            'columns.*.distinct' => 'Columns must be unique.',
            'mapping_rules.*.when.field.required' => 'Each rule must specify a "when.field".',
            'mapping_rules.*.when.op.in' => 'Unsupported operator.',
            'mapping_rules.*.then.min' => 'Each rule must have at least one THEN action.',
            'mapping_rules.*.then.*.action.in' => 'Only "set", "append", "prepend", and "merge" actions are supported.',
            'mapping_rules.*.then.*.target.required' => 'Each THEN action must specify a target field.',
        ];
    }
}
