<?php

declare(strict_types=1);

namespace app\common\service;

use think\Config;

/**
 * 分流链接号码严格轮转计数（Redis INCR，不可用时降级为 runtime 文件锁）
 */
class SplitRoundRobinStore
{
    private const KEY_PREFIX = 'split:rr:';

    /** @var \Redis|null */
    private $redis = null;

    private bool $redisReady = false;

    private bool $redisAttempted = false;

    /**
     * 获取本次访问应使用的号码下标（0 .. count-1）
     */
    public function nextIndex(int $splitLinkId, int $numberCount): int
    {
        if ($splitLinkId <= 0 || $numberCount <= 0) {
            return 0;
        }

        if ($this->ensureRedis()) {
            $key = self::KEY_PREFIX . $splitLinkId;
            $seq = (int) $this->redis->incr($key);
            if ($seq <= 0) {
                $seq = 1;
            }

            return ($seq - 1) % $numberCount;
        }

        return $this->nextIndexFallback($splitLinkId, $numberCount);
    }

    /**
     * 尝试连接 Redis（配置来自 queue 扩展）
     */
    private function ensureRedis(): bool
    {
        if ($this->redisAttempted) {
            return $this->redisReady;
        }
        $this->redisAttempted = true;

        if (!extension_loaded('redis')) {
            return false;
        }

        $config = Config::get('queue');
        if (!is_array($config) || ($config['connector'] ?? '') !== 'Redis') {
            $appPath = defined('APP_PATH') ? APP_PATH : (defined('ROOT_PATH') ? ROOT_PATH . 'application/' : '');
            $file = $appPath . 'extra/queue.php';
            if (is_file($file)) {
                $loaded = include $file;
                $config = is_array($loaded) ? $loaded : [];
            } else {
                $config = [];
            }
        }

        $host = (string) ($config['host'] ?? '127.0.0.1');
        $port = (int) ($config['port'] ?? 6379);
        $password = (string) ($config['password'] ?? '');
        $select = (int) ($config['select'] ?? 0);
        $timeout = (float) ($config['timeout'] ?? 1.0);

        try {
            $redis = new \Redis();
            $connected = $timeout > 0
                ? @$redis->connect($host, $port, $timeout)
                : @$redis->connect($host, $port);
            if (!$connected) {
                return false;
            }
            if ($password !== '') {
                if (!$redis->auth($password)) {
                    return false;
                }
            }
            if ($select > 0) {
                $redis->select($select);
            }
            $this->redis = $redis;
            $this->redisReady = true;
        } catch (\Throwable $e) {
            $this->redisReady = false;
        }

        return $this->redisReady;
    }

    /**
     * 无 Redis 时使用 runtime 文件锁递增（开发/单机可用；生产请启用 Redis）
     */
    private function nextIndexFallback(int $splitLinkId, int $numberCount): int
    {
        $runtime = defined('RUNTIME_PATH') ? RUNTIME_PATH : (dirname(__DIR__, 3) . '/runtime/');
        $dir = $runtime . 'split_rr/';
        if (!is_dir($dir) && !@mkdir($dir, 0755, true)) {
            return 0;
        }
        $file = $dir . $splitLinkId . '.cnt';
        $fp = @fopen($file, 'c+');
        if ($fp === false) {
            return 0;
        }
        if (!flock($fp, LOCK_EX)) {
            fclose($fp);
            return 0;
        }
        $raw = stream_get_contents($fp);
        $seq = (int) $raw;
        $seq++;
        ftruncate($fp, 0);
        rewind($fp);
        fwrite($fp, (string) $seq);
        fflush($fp);
        flock($fp, LOCK_UN);
        fclose($fp);

        return ($seq - 1) % $numberCount;
    }
}
