<?php

declare(strict_types=1);

namespace Apexl\Vault\Results;

use Apexl\Vault\Interfaces\ResultInterface;
use stdClass;

/**
 * Rows already materialised in memory — the shape non-PDO drivers produce, and the
 * natural answer for a query a driver can satisfy without going to the database.
 *
 * Sequential fetch() walks the set; fetchAll*() returns the whole set and parks the
 * walker at the end, mirroring PDO rather than returning "the remainder".
 */
final class ArrayResult implements ResultInterface
{
    private int $position = 0;

    /** @param array<int, array<string, mixed>>|null $rows null means "no query has run" */
    public function __construct(private readonly ?array $rows = null) {}

    public function fetch(): stdClass|false
    {
        $row = $this->fetchAssoc();

        return $row === false ? false : (object) $row;
    }

    public function fetchColumn(int $ix = 0): mixed
    {
        $row = $this->fetchAssoc();
        if ($row === false) {
            return false;
        }

        // Stable column-by-index requires the row to remember its key order, which
        // array_values() discards. Acceptable for the common cases (a count query
        // returns [['count' => N]], so fetchColumn(0) === N).
        return array_values($row)[$ix] ?? false;
    }

    public function fetchAll(): array|false
    {
        $rows = $this->fetchAllAssoc();
        if ($rows === false) {
            return false;
        }

        return array_map(static fn(array $row): stdClass => (object) $row, $rows);
    }

    public function fetchAllAssoc(): array|false
    {
        if ($this->rows === null) {
            return false;
        }
        $this->position = count($this->rows);

        return $this->rows;
    }

    public function fetchAssoc(): array|false
    {
        if ($this->rows === null || $this->position >= count($this->rows)) {
            return false;
        }

        return $this->rows[$this->position++];
    }

    public function rowCount(): int
    {
        return count($this->rows ?? []);
    }
}
