<?php

declare(strict_types=1);

namespace PrettyLinks\Pro\Rotation;

// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery
// phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching

/**
 * Round-robin rotation. Tracks position via an atomic MySQL INSERT … ON DUPLICATE KEY
 * UPDATE in wp_options so concurrent requests cannot read the same counter value.
 */
class Sequential implements RotationStrategy
{
    /**
     * Picks the next rotation row round-robin, advancing an atomic counter.
     *
     * @param  integer                          $linkId    Link ID being rotated.
     * @param  array<int, array<string, mixed>> $rotations Rows from prli_link_rotations.
     * @return array<string, mixed>|null
     */
    public function pick(int $linkId, array $rotations): ?array
    {
        global $wpdb;

        $count = count($rotations);
        if ($count === 0) {
            return null;
        }

        $optKey = 'prli_rot_pos_' . $linkId;

        // Atomic increment: MySQL evaluates option_value + 1 before any other
        // connection can read the same value, so each request advances the
        // counter without a read-modify-write race.
        $wpdb->query(
            $wpdb->prepare(
                "INSERT INTO {$wpdb->options} (option_name, option_value, autoload)
                 VALUES (%s, 1, 'no')
                 ON DUPLICATE KEY UPDATE option_value = option_value + 1",
                $optKey
            )
        );

        // Read straight from the row, NOT get_option(): the raw query above
        // never primed the options cache, so on a persistent object cache
        // get_option() would return a stale (or absent) value and hand every
        // visitor the same position.
        $pos = (int) $wpdb->get_var(
            $wpdb->prepare(
                "SELECT option_value FROM {$wpdb->options} WHERE option_name = %s",
                $optKey
            )
        );
        $idx = $pos > 0 ? ($pos - 1) % $count : 0;
        $row = array_values($rotations)[$idx] ?? null;

        return is_array($row) ? $row : null;
    }
}
