<?php

declare(strict_types=1);

namespace app\common\library;

use app\common\model\MallBusinessConfig;
use support\Redis as RedisSupport;
use Throwable;

/**
 * PlayX 与积分商城相关的比例配置：优先读 Redis，未命中则读 mall_business_config 并回写 Redis；无表记录时回退 .env 并写入 Redis。
 */
final class MallPlayxRatios
{
    public const REDIS_KEY = 'mall:business_config:playx_ratios';

    /**
     * @return array{return_ratio: float, unlock_ratio: float, points_to_cash_ratio: float}
     */
    public static function get(): array
    {
        $conn = self::redisConnection();
        if ($conn !== null) {
            $cached = self::readFromRedis($conn);
            if ($cached !== null) {
                return $cached;
            }
        }

        return self::loadFromDatabaseAndWriteRedis($conn);
    }

    /**
     * @param array{return_ratio?: mixed, unlock_ratio?: mixed, points_to_cash_ratio?: mixed} $ratios
     */
    public static function syncToRedis(array $ratios): void
    {
        $payload = [
            'return_ratio'          => self::toFloat($ratios['return_ratio'] ?? 0),
            'unlock_ratio'          => self::toFloat($ratios['unlock_ratio'] ?? 0),
            'points_to_cash_ratio' => self::toFloat($ratios['points_to_cash_ratio'] ?? 0),
        ];
        self::writeRedis(self::redisConnection(), $payload);
    }

    public static function syncFromRow(MallBusinessConfig $row): void
    {
        self::syncToRedis([
            'return_ratio'          => $row->return_ratio,
            'unlock_ratio'          => $row->unlock_ratio,
            'points_to_cash_ratio' => $row->points_to_cash_ratio,
        ]);
    }

    public static function forget(): void
    {
        $conn = self::redisConnection();
        if ($conn === null) {
            return;
        }
        try {
            $conn->del(self::REDIS_KEY);
        } catch (Throwable) {
        }
    }

    private static function redisConnection(): ?object
    {
        $cfg = config('redis');
        if (!is_array($cfg) || empty($cfg)) {
            return null;
        }
        try {
            return RedisSupport::connection('default');
        } catch (Throwable) {
            return null;
        }
    }

    /**
     * @return array{return_ratio: float, unlock_ratio: float, points_to_cash_ratio: float}|null
     */
    private static function readFromRedis(object $conn): ?array
    {
        try {
            $raw = $conn->get(self::REDIS_KEY);
        } catch (Throwable) {
            return null;
        }
        if (!is_string($raw) || $raw === '') {
            return null;
        }
        $data = json_decode($raw, true);
        if (!is_array($data)) {
            return null;
        }
        if (!isset($data['return_ratio'], $data['unlock_ratio'], $data['points_to_cash_ratio'])) {
            return null;
        }

        return [
            'return_ratio'          => self::toFloat($data['return_ratio']),
            'unlock_ratio'          => self::toFloat($data['unlock_ratio']),
            'points_to_cash_ratio' => self::toFloat($data['points_to_cash_ratio']),
        ];
    }

    /**
     * @return array{return_ratio: float, unlock_ratio: float, points_to_cash_ratio: float}
     */
    private static function loadFromDatabaseAndWriteRedis(?object $conn): array
    {
        $row = MallBusinessConfig::order('id', 'asc')->find();
        if ($row) {
            $out = [
                'return_ratio'          => self::toFloat($row->return_ratio),
                'unlock_ratio'          => self::toFloat($row->unlock_ratio),
                'points_to_cash_ratio' => self::toFloat($row->points_to_cash_ratio),
            ];
            self::writeRedis($conn, $out);

            return $out;
        }

        $out = [
            'return_ratio'          => floatval(env('PLAYX_RETURN_RATIO', '0.1')),
            'unlock_ratio'          => floatval(env('PLAYX_UNLOCK_RATIO', '0.1')),
            'points_to_cash_ratio' => floatval(env('PLAYX_POINTS_TO_CASH_RATIO', '0.1')),
        ];
        self::writeRedis($conn, $out);

        return $out;
    }

    /**
     * @param array{return_ratio: float, unlock_ratio: float, points_to_cash_ratio: float} $payload
     */
    private static function writeRedis(?object $conn, array $payload): void
    {
        if ($conn === null) {
            return;
        }
        try {
            $json = json_encode($payload, JSON_THROW_ON_ERROR);
            $conn->set(self::REDIS_KEY, $json);
        } catch (Throwable) {
        }
    }

    private static function toFloat(mixed $v): float
    {
        if (is_numeric($v)) {
            return floatval($v);
        }

        return 0.0;
    }
}
