<?php

namespace App\Service\Prices;

use App\Service\Adapter\RedisClientService;
use Redis;

class RedisReadService
{
    private ?Redis $redis = null;
    public const RAW_DATA = 'RAW_DATA';
    public const RAW_DATA_DIFF_STEPS = 'RAW_DATA_DIFF_STEPS';
    public const RAW_DATA_WITH_ARTICLE = 'RAW_DATA_WITH_ARTICLE';

    public function __construct(private readonly RedisClientService $redisClientService)
    {
        //$this->redis = $clientService->get();
    }

    /**
     * Retrieves prices for the given SKUs based on the current parameters and call type.
     *
     * @param array $currentParams The current parameters including SKU, shop ID, step, AK step, max price age, and no tax flag.
     * @param string $currentCallType The type of call being made (e.g., RAW_DATA, RAW_DATA_DIFF_STEPS).
     * @return array An associative array containing the success status, message, data, total count, and validity of MTO prices.
     */
    public function getPricesForSkus(array $currentParams, string $currentCallType): array
    {
        $skus = $currentParams['sku'];
        $shopId = $currentParams['shop'];
        $step = $currentParams['step'];
        $akStep = $currentParams['ak_step'];
        $maxPriceAge = $currentParams['maxPriceAge'];
        $noTax = $currentParams['noTax'] ?? false;

        $this->validateCallType($currentCallType);

        return $this->getPrices($skus, $shopId, $step, $akStep, $maxPriceAge, $noTax);
    }

    private function validateCallType(string $callType): void
    {
        $validCallTypes = [
            self::RAW_DATA,
            self::RAW_DATA_DIFF_STEPS,
            self::RAW_DATA_WITH_ARTICLE
        ];

        if (!in_array($callType, $validCallTypes, true)) {
            throw new \InvalidArgumentException("Invalid call type: $callType");
        }
    }

    /**
     * Retrieves prices for the given SKUs from Redis.
     *
     * @param array $skus List of SKUs to retrieve prices for.
     * @param int $shopId The ID of the shop.
     * @param int $step The price step to retrieve.
     * @param int|null $akStep Optional AK step to retrieve.
     * @param int|null $maxPriceAge Optional maximum age of the price data in seconds.
     * @param bool $noTax Whether to exclude tax information from the result.
     * @return array An associative array containing the success status, message, data, total count, and validity of MTO prices.
     */
    public function getPrices(array $skus, int $shopId, int $step, ?int $akStep = null, ?int $maxPriceAge = null, bool $noTax = false): array
    {
        $microNow = microtime(true);
        $version = $this->redis()->get("current_cache_version:$shopId");

        if (!$version) {
            return [
                'success' => false,
                'message' => 'No active version found for shop',
                'data' => [],
                'total' => 0,
                'MTO-PRICES-VALID' => false,
                'showTax' => $noTax
            ];
        }

        $result = [];
        $mto_price_valid = true;
        $now = time();
        $chunkSize = 100;

        // Preisdaten in Chunks laden
        $priceDataAll = [];
        foreach (array_chunk($skus, $chunkSize) as $chunk) {
            if (!is_array($chunk)) continue;
            $keys = array_map(fn($sku) => "v{$version}:{$step}:{$shopId}:{$sku}", $chunk);
            $priceDataAll = array_merge($priceDataAll, $this->redis()->mget($keys));
        }

        // Optional: AK-Daten in Chunks laden
        $akDataAll = [];
        if ($akStep !== null) {
            foreach (array_chunk($skus, $chunkSize) as $chunk) {
                if (!is_array($chunk)) continue;
                $akKeys = array_map(fn($sku) => "v{$version}:{$akStep}:{$shopId}:{$sku}", $chunk);
                $akDataAll = array_merge($akDataAll, $this->redis()->mget($akKeys));
            }
        }

        foreach ($skus as $index => $sku) {
            $entry = $priceDataAll[$index] ? json_decode($priceDataAll[$index], true) : null;
            if (!$entry) {
                continue;
            }

            $ak = $akDataAll[$index] ?? null;
            if ($ak && is_string($ak)) {
                $ak = json_decode($ak, true);
            }

            $unit = str_starts_with($sku, 'BR-') ? 'Karat' : 'Gramm';
            $is_disabled = false;

            if ($maxPriceAge !== null && ($now - $entry['timestamp']) > $maxPriceAge) {
                $mto_price_valid = false;
                $is_disabled = true;
            }

            if (!$entry['vk_price']) {
                $mto_price_valid = false;
                $is_disabled = true;
            }

            $result[$sku] = [
                'subshopId' => $shopId,
                'sku' => $sku,
                'step' => $step,

                'price_net' => $entry['vk_price'],
                'price_num' => $entry['vk_price_gross'] ?? null,
                'price_currency' => $this->formatCurrency($entry['vk_price_gross'] ?? 0.0),

                'ref_price_net' => $entry['vk_g_price'],
                'ref_price_num' => $entry['vk_g_price_gross'],
                'ref_price_currency' => '(' . $this->formatCurrency($entry['vk_g_price_gross']) . ' / 1 ' . $unit . ')',
                'ref_price_currency_raw' => $this->formatCurrency($entry['vk_g_price_gross']),

                'ak_step' => $akStep ?? $step,
                'ak_price_net' => $ak['ak_price'] ?? $entry['ak_price'],
                'ak_price_num' => $ak['ak_price'] ?? $entry['ak_price'],
                'ak_price_currency' => $this->formatCurrency($ak['ak_price'] ?? $entry['ak_price'] ?? 0.00),

                'cash_price_net' => $entry['cash_price'],
                'cash_price_num' => $entry['cash_price_gross'] ?? 0.0,
                'cash_price_currency' => $this->formatCurrency($entry['cash_price_gross'] ?? 0.0),

                'is_disabled' => $is_disabled,

                'taxrate' => round($entry['taxrate'] * 100),
                'version' => $version,
                'last_update' => date('d.m.Y H:i:s', strtotime($entry['last_update'])),
                'timestamp' => $entry['timestamp'],
                'timestampAsDate' => date('d.m.Y H:i:s', $entry['timestamp']),
                'updateStartTimestamp' => date('d.m.Y H:i:s', $entry['update_start_timestamp']),
                'updateEndTimestamp' => date('d.m.Y H:i:s', $entry['update_end_timestamp']),
            ];
        }

        $timeGap = (microtime(true) - $microNow) * 1000;

        return [
            'success' => true,
            'total' => count($result),
            'version' => $version,
            'MTO-PRICES-VALID' => $mto_price_valid,
            'timeGap' => round($timeGap, 2) . ' ms',
            'data' => $result,
            'showTax' => $noTax
        ];
    }


    /**
     * Formats a float value as a currency string.
     *
     * @param float $value The value to format.
     * @return string The formatted currency string.
     */
    private function formatCurrency(float $value): string
    {
        return number_format($value, 2, ',', '.') . ' €';
    }

    /**
     * Returns the Redis client instance.
     *
     * @return Redis
     */
    private function redis(): Redis
    {
        if ($this->redis === null) {
            $this->redis = $this->redisClientService->get();
        }
        return $this->redis;
    }
}
