<?php

declare(strict_types=1);

namespace App\Casts;

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

/**
 * Eloquent cast for PostgreSQL native INT[] columns.
 *
 * Laravel stock 'array' cast uses json_encode/json_decode and sends `[1,2,3]`
 * (JSON), which Postgres rejects on INT[] columns (expects `{1,2,3}` array
 * literal). This cast:
 *
 *  - get(): parses Postgres array literal `{1,2,3}` (or empty `{}`) into PHP
 *           int array.
 *  - set(): serializes PHP array `[1,2,3]` into Postgres literal `{1,2,3}`.
 *
 * Used for projects.regions INT[] (Plan 6).
 *
 * @implements CastsAttributes<list<int>, list<int>|null>
 */
class PostgresIntArray implements CastsAttributes
{
    /**
     * @param  array<string, mixed>  $attributes
     * @return list<int>
     */
    public function get(Model $model, string $key, mixed $value, array $attributes): array
    {
        if ($value === null || $value === '' || $value === '{}') {
            return [];
        }

        // PG returns literal like "{1,2,3}".
        if (is_string($value)) {
            $trimmed = trim($value, '{}');

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

            return array_map('intval', explode(',', $trimmed));
        }

        // Defensive: if driver already gave array.
        if (is_array($value)) {
            return array_values(array_map('intval', $value));
        }

        return [];
    }

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

        // Defensive: interface phpdoc says list<int>|null, but $value is mixed at PHP level;
        // protect against runtime misuse (e.g., string passed mistakenly).
        // @phpstan-ignore function.alreadyNarrowedType
        if (! is_array($value)) {
            throw new \InvalidArgumentException(
                "PostgresIntArray cast expects array for key '{$key}', got ".gettype($value)
            );
        }

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

        $ints = array_map('intval', $value);

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