"""Tests for the in-memory TTL result cache."""

import time

import pandas as pd
import pytest

from akshare_cli.core.cache import (
    DEFAULT_TTL,
    NO_CACHE,
    ResultCache,
    _result_cache,
    get_ttl,
)


# ---------------------------------------------------------------------------
# get_ttl()
# ---------------------------------------------------------------------------

class TestGetTtl:
    def test_no_cache_function_returns_zero(self):
        for name in ("stock_zh_a_spot_em", "stock_zh_a_spot", "forex_spot_em"):
            assert get_ttl(name) == 0

    def test_custom_ttl(self):
        assert get_ttl("futures_hist_table_em") == 86400

    def test_default_ttl(self):
        assert get_ttl("some_unknown_func") == DEFAULT_TTL


# ---------------------------------------------------------------------------
# ResultCache
# ---------------------------------------------------------------------------

class TestResultCache:
    def setup_method(self):
        self.cache = ResultCache()

    def test_miss_returns_none(self):
        assert self.cache.get("foo", {"a": 1}) is None
        assert self.cache.misses == 1

    def test_put_then_hit(self):
        df = pd.DataFrame({"x": [1, 2, 3]})
        self.cache.put("foo", {"a": 1}, df, ttl=60)
        result = self.cache.get("foo", {"a": 1})
        assert result is not None
        assert list(result["x"]) == [1, 2, 3]
        assert self.cache.hits == 1

    def test_different_kwargs_miss(self):
        self.cache.put("foo", {"a": 1}, "data1", ttl=60)
        assert self.cache.get("foo", {"a": 2}) is None

    def test_ttl_expiry(self):
        self.cache.put("foo", {}, "data", ttl=1)
        assert self.cache.get("foo", {}) == "data"
        time.sleep(1.1)
        assert self.cache.get("foo", {}) is None

    def test_disabled_cache_never_hits(self):
        self.cache.enabled = False
        self.cache.put("foo", {}, "data", ttl=60)
        assert self.cache.get("foo", {}) is None

    def test_zero_ttl_not_stored(self):
        self.cache.put("foo", {}, "data", ttl=0)
        assert self.cache.get("foo", {}) is None

    def test_clear(self):
        self.cache.put("a", {}, 1, ttl=60)
        self.cache.put("b", {}, 2, ttl=60)
        self.cache.get("a", {})  # hit
        self.cache.clear()
        assert self.cache.get("a", {}) is None
        assert self.cache.hits == 0
        assert self.cache.misses == 1  # the get after clear

    def test_stats(self):
        self.cache.put("x", {}, "d", ttl=60)
        self.cache.get("x", {})   # hit
        self.cache.get("y", {})   # miss
        stats = self.cache.stats()
        assert stats["entries"] == 1
        assert stats["hits"] == 1
        assert stats["misses"] == 1
        assert stats["hit_rate"] == "50.0%"

    def test_stats_no_requests(self):
        stats = self.cache.stats()
        assert stats["hit_rate"] == "N/A"


# ---------------------------------------------------------------------------
# Global singleton reset safety
# ---------------------------------------------------------------------------

class TestGlobalCache:
    def setup_method(self):
        _result_cache.clear()
        _result_cache.enabled = True

    def teardown_method(self):
        _result_cache.clear()
        _result_cache.enabled = True

    def test_global_put_get(self):
        _result_cache.put("test_fn", {"k": "v"}, [1, 2], ttl=60)
        assert _result_cache.get("test_fn", {"k": "v"}) == [1, 2]
