<?php

namespace App\Casts;

use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Database\Eloquent\Model;

/**
 * Cast para columnas text[] de Postgres.
 *
 * Laravel no trae uno: 'array' asume JSON y sobre un text[] devuelve null.
 * Lo usan items.images y tasks.images, que llegan como '{a,b}'.
 *
 * @implements CastsAttributes<array<int, string>, array<int, string>|null>
 */
class PostgresArray implements CastsAttributes
{
    /**
     * @return array<int, string>
     */
    public function get(Model $model, string $key, mixed $value, array $attributes): array
    {
        if ($value === null || $value === '' || $value === '{}') {
            return [];
        }

        if (is_array($value)) {
            return $value;
        }

        $literal = (string) $value;

        if (! str_starts_with($literal, '{') || ! str_ends_with($literal, '}')) {
            return [];
        }

        $inner = substr($literal, 1, -1);

        if ($inner === '') {
            return [];
        }

        return $this->parse($inner);
    }

    /**
     * Parser del literal de array de Postgres.
     *
     * No se usa str_getcsv: PHP conserva el carácter de escape y obliga a
     * limpiarlo después, lo que rompe los valores con comillas embebidas.
     * Postgres cita un elemento cuando contiene , { } " \ o espacios, y
     * dentro de las comillas escapa " y \ con barra invertida.
     *
     * @return array<int, string>
     */
    private function parse(string $inner): array
    {
        $result = [];
        $current = '';
        $inQuotes = false;
        $length = strlen($inner);

        for ($i = 0; $i < $length; $i++) {
            $char = $inner[$i];

            if ($inQuotes) {
                if ($char === '\\' && $i + 1 < $length) {
                    $current .= $inner[++$i];
                } elseif ($char === '"') {
                    $inQuotes = false;
                } else {
                    $current .= $char;
                }

                continue;
            }

            if ($char === '"') {
                $inQuotes = true;
            } elseif ($char === ',') {
                $result[] = $current;
                $current = '';
            } else {
                $current .= $char;
            }
        }

        $result[] = $current;

        return $result;
    }

    public function set(Model $model, string $key, mixed $value, array $attributes): ?string
    {
        if ($value === null) {
            return null;
        }

        $values = is_array($value) ? $value : [$value];

        if ($values === []) {
            return '{}';
        }

        $escaped = array_map(
            static fn ($item): string => '"'.addcslashes((string) $item, '"\\').'"',
            $values
        );

        return '{'.implode(',', $escaped).'}';
    }
}
