<?php

namespace App\Http\Requests\Api\Admin\V1;

use App\Http\Resources\Admin\V1\ClientResource;
use App\Models\Clients\Client;

/**
 * Crear (`POST /clients`) o editar (`PATCH /clients/{client}`) un cliente.
 *
 * **Las reglas son las del panel**, de {@see Client::reglasBasicas()}: dos puertas para
 * dar de alta clientes no pueden tener dos ideas distintas de qué es un shortname válido.
 *
 * Se aceptan los campos de la ficha del panel —{@see Client::reglasDeLaFicha()}— y nada
 * más. Lo demás se ignora; en especial `actived`, porque la baja de un cliente corta el
 * servicio de todos sus sitios y se hace desde el panel, con su motivo.
 *
 * Lo que se escribe, se devuelve: {@see ClientResource} saca los mismos campos.
 */
class ClienteRequest extends PeticionDeAdministracion
{
    /**
     * **Los dos únicos alias de la API**: nombre en la API → columna.
     *
     * El resto de campos se llaman como su columna, pero estas dos están en castellano y en
     * un contrato en inglés quedaban mal. Se traducen solo aquí y en {@see ClientResource}:
     * la base de datos y el panel no cambian. Decidido con Antonio el 2026-10-30.
     */
    public const ALIAS = [
        'legal_name' => 'razon_social',
        'relationship_start_date' => 'fecha_inicio_relacion',
    ];

    /** Los campos que acepta, con su nombre en la API. */
    public const CAMPOS = [
        'name', 'shortname', 'crm_id', 'cif',
        'legal_name', 'relationship_start_date', 'contacts', 'description', 'jira', 'observation',
    ];

    private function cliente(): ?Client
    {
        $cliente = $this->route('client');

        return $cliente instanceof Client ? $cliente : null;
    }

    /**
     * Un `shortname` o un `crm_id` repetido es un 409 con el cliente que ya lo tiene, **antes
     * de validar**.
     *
     * Así un script que reintenta un alta que ya se hizo recibe el cliente en vez de un
     * error de formulario. Se miran también los **borrados en blando**: el índice único de
     * la base no los distingue, así que su shortname sigue ocupado. De esos no se devuelve
     * nada, porque por la API no se pueden ver.
     */
    protected function prepareForValidation(): void
    {
        $this->quitarContactosVacios();

        $propio = $this->cliente()?->id;

        foreach (['shortname', 'crm_id'] as $campo) {
            $valor = $this->input($campo);

            if (! is_string($valor) || trim($valor) === '') {
                continue;
            }

            $existente = Client::withTrashed()
                ->where($campo, $valor)
                ->when($propio !== null, fn ($q) => $q->whereKeyNot($propio))
                ->first();

            if ($existente === null) {
                continue;
            }

            if ($existente->trashed()) {
                $this->conflicto(
                    'Ya hay un cliente con ese ' . $campo . ', pero está borrado: el valor sigue '
                    . 'ocupado porque el índice de la base cuenta los borrados. Se resuelve desde el panel.'
                );
            }

            $this->conflicto(
                'Ya existe un cliente con ese ' . $campo . '.',
                ClientResource::make($existente)->resolve($this)
            );
        }
    }

    /**
     * Las filas de contacto sin nada escrito se tiran antes de validar, como en el panel
     * (`HasClientValidation::limpiarContactosVacios()`). Una con algo escrito sí se valida.
     */
    private function quitarContactosVacios(): void
    {
        $contactos = $this->input('contacts');

        if (! is_array($contactos)) {
            return;
        }

        $this->merge(['contacts' => array_values(array_filter(
            $contactos,
            fn ($contacto) => ! is_array($contacto)
                || trim((string) ($contacto['name'] ?? '')) !== ''
                || trim((string) ($contacto['email'] ?? '')) !== ''
                || trim((string) ($contacto['phone'] ?? '')) !== ''
        ))]);
    }

    /**
     * Lo validado y aceptado, listo para el modelo: **con el nombre de la columna**, ya
     * traducidos los alias. Sin contactos se guarda `null`, como el panel
     * (`normalizeContacts()`).
     *
     * @return array<string, mixed>
     */
    public function datos(): array
    {
        $datos = [];

        foreach ($this->safe()->only(self::CAMPOS) as $campo => $valor) {
            $datos[self::ALIAS[$campo] ?? $campo] = $valor;
        }

        if (array_key_exists('contacts', $datos) && $datos['contacts'] === []) {
            $datos['contacts'] = null;
        }

        return $datos;
    }

    public function rules(): array
    {
        // Las reglas del panel, con las claves que usa la API: así los errores salen con el
        // nombre que se envió.
        $columnas = array_flip(self::ALIAS);
        $reglas = [];

        foreach ([...Client::reglasBasicas($this->cliente()?->id), ...Client::reglasDeLaFicha()] as $campo => $regla) {
            $reglas[$columnas[$campo] ?? $campo] = $regla;
        }

        // Al editar, solo los campos que lleguen.
        if ($this->isMethod('PATCH')) {
            $reglas = array_map(fn (string $regla) => 'sometimes|' . $regla, $reglas);
        }

        return $reglas;
    }

    public function messages(): array
    {
        return [
            'name.required' => 'El nombre del cliente es obligatorio.',
            'shortname.required' => 'El shortname es obligatorio: es el identificador corto del cliente.',
            'shortname.max' => 'El shortname no puede pasar de 50 caracteres.',
            'shortname.unique' => 'Ese shortname ya está en uso.',
            'crm_id.unique' => 'Ese identificador de CRM ya está en otro cliente.',
            'cif.max' => 'El CIF no puede pasar de 20 caracteres.',
            'contacts.array' => 'contacts va como una lista de {name, email, phone}.',
            'contacts.*.name.required' => 'Cada contacto necesita un nombre.',
            'contacts.*.email.email' => 'Ese correo de contacto no tiene una forma válida.',
            'relationship_start_date.date' => 'La fecha de inicio de la relación no es una fecha válida (AAAA-MM-DD).',
            'legal_name.max' => 'La razón social no puede pasar de 200 caracteres.',
        ];
    }
}
