<?php

if (!defined('ABSPATH')) exit;

use function Env\env;

class Rdg_Blocks_Cache
{
    private $cache;

    function __construct() {
        $this->cache = new Redis();
    }

    public function getRedisConnect() : ?bool {
        try {
            if (!$this->cache->ping()) {
                $this->cache->connect(WP_REDIS_HOST, WP_REDIS_PORT);
            }

            return true;
        } catch (\Exception $exception) {
            //
        }

        try {
            $this->cache->auth(WP_REDIS_PASSWORD);

            if (!$this->cache->ping()) {
                $this->cache->connect(WP_REDIS_HOST, WP_REDIS_PORT);
            }

            return true;
        } catch (\Exception $exception) {
            error_log(print_r([
                'exception' => sprintf('RDG Blocks / getRedisConnect: %s', $exception->getMessage()),
            ], true));
        }

        return null;
    }

    public function getBlocksCacheListName() : string {
        return RDGBLOCKS_NAME_SLUG.':cache';
    }

    public function getCacheFailedName($name) : string {
        return $name.':failed';
    }

    public function getCacheList() : array {
        if (!is_null($this->cache)) {
            return $this->cache->lrange($this->getBlocksCacheListName(), 0, -1);
        }

        return [];
    }

    public function addCacheList($name, $refresh, $data) : bool {
        try {
            if (!is_null($this->cache)) {
                $this->cache->rpush($this->getBlocksCacheListName(), $name);
                $this->cache->set($name, json_encode($data), $refresh * 60 * 60);
                
                return true;
            }
        } catch (\Exception $exception) {
            error_log(print_r([
                'exception' => sprintf('RDG Blocks / addCacheList: %s', $exception->getMessage()),
                'name' => $name,
                'refresh' => $refresh,
                'data' => $data,
            ], true));
        }

        return false;
    }

    public function updateCacheFailed($name, $cache_refresh) : int|null {
        try {
            if (!is_null($this->cache)) {
                $failed_name = $this->getCacheFailedName($name);
                $cache_failed_exist = $this->cache->get($failed_name);
                $this->cache->expire($name, $cache_refresh * 60 * 60);

                if (empty($cache_failed_exist)) {
                    $this->cache->incr($failed_name);
                    $this->cache->expire($failed_name, RDG_BLOCK_CACHE_FAILED_RETRY_DELAY);

                    return 1;
                }

                $this->cache->incr($failed_name);

                return $cache_failed_exist + 1;
            }
        } catch (\Exception $exception) {
            error_log(print_r([
                'exception' => sprintf('RDG Blocks / getCacheFailed: %s', $exception->getMessage()),
                'name' => $name,
            ], true));
        }

        return null;
    }

    public function getCacheBlock($name) : bool|string {
        try {
            if (!is_null($this->cache)) {
                return $this->cache->get($name);
            }
        } catch (\Exception $exception) {
            error_log(print_r([
                'exception' => sprintf('RDG Blocks / getCacheBlock: %s', $exception->getMessage()),
                'name' => $name,
            ], true));
        }

        return false;
    }

    public function getCacheBlockTimeSeg($name) : int {
        try {
            if (!is_null($this->cache)) {
                return $this->cache->ttl($name);
            }
        } catch (\Exception $exception) {
            error_log(print_r([
                'exception' => sprintf('RDG Blocks / getCacheBlockTime: %s', $exception->getMessage()),
                'name' => $name,
            ], true));
        }

        return 0;
    }

    public function getCacheBlockTime($name) : bool|string {
        try {
            if (!is_null($this->cache)) {
                $time = $this->cache->ttl($name);

                if ($time > 3600) {
                    return round($time / 3600, 2) . 'h';
                } elseif ($time > 0) {
                    return round($time / 60, 1) . 'm';
                }
            }
        } catch (\Exception $exception) {
            error_log(print_r([
                'exception' => sprintf('RDG Blocks / getCacheBlockTime: %s', $exception->getMessage()),
                'name' => $name,
            ], true));
        }

        return false;
    }

    public function delAllCache(): bool {
        try {
            if (!is_null($this->cache)) {
                $cache_blocks = $this->getCacheList();

                foreach ($cache_blocks as $saved_cache) {
                    $this->delCacheBlock($saved_cache);
                }

                $this->cache->del($this->getBlocksCacheListName());

                return true;
            }
        } catch (\Exception $exception) {
            error_log(print_r([
                'exception' => sprintf('RDG Blocks / delAllCache: %s', $exception->getMessage()),
            ], true));
        }

        return false;
    }

    public function delCacheBlock($name) : bool {
        try {
            if (!is_null($this->cache)) {
                $this->cache->del($name);
                $this->cache->lrem($this->getBlocksCacheListName(), $name);

                $failed_name = $this->getCacheFailedName($name);
                $this->cache->del($failed_name);

                return true;
            }
        } catch (\Exception $exception) {
            error_log(print_r([
                'exception' => sprintf('RDG Blocks / delCacheBlock: %s', $exception->getMessage()),
                'name' => $name,
            ], true));
        }

        return false;
    }

    public function updateCacheBlock($name, $attributes, $data) : bool {
        try {
            if (!is_null($this->cache)) {
                $refresh = !empty($attributes['api_attributes']['cache_refresh']) ? $attributes['api_attributes']['cache_refresh'] : 1;
                $data_update['api_response'] = $data;
                $data_update['api_attributes'] = $attributes['api_attributes'];                            
                $data_update['items'] = is_countable($data) ? count($data) : 0;

                $this->cache->set($name, json_encode($data_update), $refresh * 60 * 60);

                $failed_name = $this->getCacheFailedName($name);
                $this->cache->del($failed_name);

                return true;
            }
        } catch (\Exception $exception) {
            error_log(print_r([
                'exception' => sprintf('RDG Blocks / updateCacheBlock: %s', $exception->getMessage()),
                'name' => $name,
            ], true));
        }

        return false;
    }

    public function clearCacheBlock($blocks_list, $page): void {
        try {
            if (!is_null($this->cache)) {
                $in_array_apply = (is_array($blocks_list) && count($blocks_list) > 0) ? true : false;
                $cache_blocks = $this->getCacheList();

                foreach ($cache_blocks as $saved_cache) {
                    $params = explode(':', $saved_cache);
                    $page_saved = $params[1].':'.$params[2];

                    if ($page == $page_saved && ($in_array_apply && !in_array($saved_cache, $blocks_list) || !$in_array_apply)) {
                        $this->delCacheBlock($saved_cache);
                    }
                }
            }
        } catch (\Exception $exception) {
            error_log(print_r([
                'exception' => sprintf('RDG Blocks / clearCacheBlock: %s', $exception->getMessage()),
                'page' => $page,
                'blocks_list' => $blocks_list,
            ], true));
        }
    }
}
