import asyncio
import hashlib
import redis
import os
import pickle

from aiohttp import ClientSession

from chemdatareader.chemdatareader_logger import get_logger


def sha512_hash(text: str):
    return hashlib.sha256(text.encode("UTF-8")).hexdigest()


class CachedRequest:
    def __init__(
        self,
        redis_host: str = "localhost",
        redis_port: int = 6379,
        redis_db: str = "",
        redis_timeout: int = 604800,
        request_delay: float = 0.1,
    ) -> None:
        self.redis_host = os.getenv("REDIS_HOST", redis_host)
        self.redis_port = os.getenv("REDIS_PORT", redis_port)
        self.redis_user = os.getenv("REDIS_USER", "")
        self.redis_password = os.getenv("REDIS_PASSWORD", "")
        self.redis_db = os.getenv("REDIS_DB", redis_db)

        self.redis_timeout = os.getenv("REDIS_TIMEOUT", redis_timeout)
        self.request_delay = os.getenv("REQUEST_DELAY", request_delay)

        self.logger = get_logger(__name__)

        try:
            self.redis_srv = redis.Redis(host=self.redis_host, port=self.redis_port, db=self.redis_db, password=self.redis_password)
            #self.redis_srv = redis.Redis()
        except Exception as e:
            self.logger.error(f"redis connection error : {e}")

    async def get(self, url: str, session: ClientSession, **kwargs):
        if self.redis_srv is not None:
            redis_key = sha512_hash(url)

            try:
                value_redis_cached = self.redis_srv.get(redis_key)
                return pickle.loads(value_redis_cached)
            except Exception as e:
                self.logger.error(f"redis redis_key error : {e}")

                # redis value not available, fetch from url and store in redis
                try:
                    await asyncio.sleep(0.1)
                    value = await self.fetch_html(url, session, **kwargs)
                    self.redis_srv.set(redis_key, pickle.dumps(value), ex=self.redis_timeout)
                    return value
                except Exception as e:
                    self.logger.error(f"ERROR: caching to redis error : {e}")
                    raise e
        else:
            #redis server not available, fetch from url directly
            await asyncio.sleep(0.1)
            return await self.fetch_html(url, session, **kwargs)

    async def fetch_html(self, url: str, session: ClientSession, **kwargs) -> str:
        """GET request wrapper to fetch page HTML.

        kwargs are passed to `session.request()`.
        """
        await asyncio.sleep(self.request_delay)

        resp = await session.request(method="GET", url=url, **kwargs)
        resp.raise_for_status()
        # logger.info("Got response [%s] for URL: %s", resp.status, url)
        res_text = await resp.text()
        #self.logger.debug(f"Got response [{resp.status}] for URL: {url}\n: {res_text}")
        return res_text
