<?php

namespace App\Http\Requests;

use App\Enums\CSVDelimiter;
use App\Enums\ExportColumnJsonType;
use App\Enums\MappingOperator;
use App\Exports\JsonMapExport;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Support\Facades\Auth;
use Illuminate\Validation\Rule;

class UpdateExportMap extends UpdateExportMapTemplate
{
    /**
     * Determine if the user is authorized to make this request.
     */
    #[\Override]
    public function authorize(): bool
    {
        return Auth::check();
    }

    /**
     * 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 [
            'name' => ['required', 'string', 'max:255'],
            'type' => ['sometimes', 'string', Rule::in(array_keys(config('export_maps.types', [])))],
            'format' => ['required', 'string', Rule::in(config('export_maps.export_formats', []))],
            'api_url_enabled' => ['nullable', 'boolean'],
            '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'],
            'ftp_export_enabled' => ['required', 'boolean'],
            'driver' => ['required_if:ftp_export_enabled,true', 'nullable', 'string', 'max:255'],
            'host' => ['required_if:ftp_export_enabled,true', 'nullable', 'string', 'max:255'],
            'port' => ['nullable', 'integer'],
            'root' => ['required_if:ftp_export_enabled,true', 'nullable', 'string', 'max:255'],
            'username' => ['required_if:ftp_export_enabled,true', 'nullable', 'string', 'max:255'],
            'password' => ['required_if:ftp_export_enabled,true', 'nullable', 'string', 'max:255'],
            'filename' => ['required_if:ftp_export_enabled,true', 'nullable', 'string', 'max:255'],
            'ssl' => ['nullable', 'boolean'],
            'schedule_enabled' => ['required', 'boolean'],
            'schedule' => ['required_if:schedule_enabled,true', 'nullable', 'string', Rule::in([
                'daily',
                'weekly',
                'biweekly',
                'monthly'
            ])],
            'export_time' => ['required_if:schedule_enabled,true', 'nullable', 'date_format:H:i'],
            'export_day_of_week' => [
                'nullable',
                'integer',
                'between:1,7',
                Rule::requiredIf(fn () => $this->input('schedule_enabled') &&
                    in_array($this->input('schedule'), ['weekly', 'biweekly'])),
            ],
            'export_day_of_month' => [
                'nullable',
                'integer',
                'between:1,31',
                Rule::requiredIf(fn () => $this->input('schedule_enabled') &&
                    $this->input('schedule') === 'monthly'),
            ],
            '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 $validator) {
            $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.'
                );
            }

            $this->guardAgainstTaskOwnedSchedule($validator);
        });
    }

    /**
     * Coexistence guard, legacy side: enabling the legacy schedule is rejected while a
     * scheduled task's export-map action owns this map, so a map can never be delivered
     * by both schedulers.
     */
    private function guardAgainstTaskOwnedSchedule(Validator $validator): void
    {
        if (!$this->boolean('schedule_enabled')) {
            return;
        }

        $exportMapId = $this->route('export_map')?->id;

        if (!$exportMapId) {
            return;
        }

        $owningTask = \Mtc\MercuryDataModels\ScheduledTaskAction::query()
            ->where('action_type', 'export-map')
            ->where('config->export_map_id', $exportMapId)
            ->whereHas('task', fn ($query) => $query->where('enabled', true))
            ->with('task:id,name')
            ->first();

        if ($owningTask) {
            $validator->errors()->add(
                'schedule_enabled',
                __('scheduled_tasks.export_map_owned_by_task', ['task' => $owningTask->task->name])
            );
        }
    }
}
