import json

import psycopg2
import pymysql
import redis
from pymemcache import Client

from configs import SecretManager, context


class RDSConnection:
    def __init__(self, db_secret_name, access_key=None, secret_key=None, region_name=None):
        self.__region_name = region_name
        self.__access_key = access_key
        self.__secret_key = secret_key
        self.__db_secret_name = db_secret_name

    def conn(self):
        aws_secret_manager = SecretManager(self.__region_name, self.__access_key, self.__secret_key)
        database_values = aws_secret_manager.get_secret_key_value(self.__db_secret_name)

        user = database_values.get('username')
        password = database_values.get('password')
        host = database_values.get('host')
        port = int(database_values.get('port')) if database_values.get('port') else 3306
        return pymysql.connect(user=user, password=password, host=host, port=port)

    @staticmethod
    def cursor(conn):
        """ Return the cursor object """
        return conn.cursor()

    def executemany_query(self, query, values=None):
        """" Execute the query """
        conn = self.conn()
        try:
            cursor = self.cursor(conn=conn)
            cursor.executemany(query, values)
            conn.commit()
            return cursor
        finally:
            conn.close()

    def execute_query(self, query, values=None):
        """ Execute the query """
        conn = self.conn()
        try:
            cursor = self.cursor(conn=conn)
            cursor.execute(query, values)
            conn.commit()
            return cursor
        finally:
            conn.close()

    def fetchall(self, query, values=None):
        """ Execute the query and fetch all data """
        try:
            cursor = self.execute_query(query=query, values=values)
            return cursor.fetchall()
        except Exception as e:
            print(f'ERROR: {str(e)}')
            return []


class RedisConnection:
    def __init__(self, redis_secret_name, access_key=None, secret_key=None, region_name=None):
        self.__region_name = region_name
        self.__access_key = access_key
        self.__secret_key = secret_key
        self.__redis_secret_name = redis_secret_name

    def conn(self):
        aws_secret_manager = SecretManager(self.__region_name, self.__access_key, self.__secret_key)
        redis_values = aws_secret_manager.get_secret_key_value(self.__redis_secret_name)
        host = redis_values.get('host')
        port = redis_values.get('port')

        try:
            red_conn = redis.StrictRedis(host=host, port=port)
            return red_conn
        except redis.ConnectionError as e:
            print(f"Redis Connection Error: {e}")
        except redis.RedisError as e:
            print(f"Redis Error: {e}")

    def get(self, key):
        conn = self.conn()
        return conn.get(key)

    def set(self, key, data, ex_time):
        conn = self.conn()
        conn.set(key, data, ex=ex_time)


class TimescaleDBConnection:
    def __init__(self, timescaledb_secret_name, access_key=None, secret_key=None, region_name=None):
        self.__region_name = region_name
        self.__access_key = access_key
        self.__secret_key = secret_key
        self.__timescaledb_secret_name = timescaledb_secret_name

    def conn(self):
        aws_secret_manager = SecretManager(self.__region_name, self.__access_key, self.__secret_key)
        timescaledb_values = aws_secret_manager.get_secret_key_value(self.__timescaledb_secret_name)

        username = timescaledb_values.get('username')
        password = timescaledb_values.get('password')
        host = timescaledb_values.get('host')
        port = int(timescaledb_values.get('port'))
        dbname = timescaledb_values.get('dbname')

        return psycopg2.connect(user=username, password=password, host=host, port=port, database=dbname)

    def cursor(self, conn):
        """ Return a cursor object """
        return conn.cursor()

    def execute_query(self, query, values=None):
        """ Execute a single query """
        conn = self.conn()
        try:
            cursor = self.cursor(conn=conn)
            cursor.execute(query, values)
            conn.commit()
            return cursor
        finally:
            conn.close()

    def executemany_query(self, query, values=None):
        """ Execute a query with multiple parameters """
        conn = self.conn()
        try:
            cursor = self.cursor(conn=conn)
            cursor.executemany(query, values)
            conn.commit()
            return cursor
        finally:
            conn.close()

    def fetchall(self, query, values=None):
        """ Execute a query and fetch all data """
        conn = self.conn()
        try:
            cursor = self.cursor(conn=conn)
            cursor.execute(query, values)
            result = cursor.fetchall()
            return result
        except Exception as e:
            print(f'ERROR: {str(e)}')
            return []
        finally:
            conn.close()


class MemcachedConnection:
    def __init__(self, memcached_secret_name, access_key=None, secret_key=None, region_name=None):
        self.__region_name = region_name
        self.__access_key = access_key
        self.__secret_key = secret_key
        self.__memcached_secret_name = memcached_secret_name

    def get_client(self):
        aws_secret_manager = SecretManager(self.__region_name, self.__access_key, self.__secret_key)
        memcached_values = aws_secret_manager.get_secret_key_value(self.__memcached_secret_name)
        host = memcached_values.get('host')
        port = memcached_values.get('port')
        return Client((host, port), tls_context=context)

    def set_data_into_memcache(self, key, data, ttl=600, verbos=False):
        client = self.get_client()
        json_data = json.dumps(data, default=str)

        client.set(key, json_data, expire=ttl)
        if verbos:
            print(f"INFO: Data set to memcached: {data}")

    def get_data_from_memcached(self, key, verbos=False):
        client = self.get_client()
        cached_data = client.get(key)

        if cached_data:
            data = json.loads(cached_data)
            if verbos:
                print(f"INFO: Data got from memcached: {data}")
            return data, True

        return None, None
