"""
Module for storing backup related data in redis.
"""

from typing import List, Optional
from datetime import datetime

from selfprivacy_api.models.backup.snapshot import Snapshot
from selfprivacy_api.models.backup.provider import BackupProviderModel
from selfprivacy_api.graphql.common_types.backup import (
    AutobackupQuotas,
    _AutobackupQuotas,
)

from selfprivacy_api.utils.redis_pool import RedisPool
from selfprivacy_api.utils.redis_model_storage import (
    store_model_as_hash,
    hash_as_model,
)

from selfprivacy_api.backup.providers.provider import AbstractBackupProvider
from selfprivacy_api.backup.providers import get_kind

REDIS_SNAPSHOTS_PREFIX = "backups:snapshots:"
REDIS_LAST_BACKUP_PREFIX = "backups:last-backed-up:"
REDIS_INITTED_CACHE = "backups:repo_initted"

REDIS_PROVIDER_KEY = "backups:provider"
REDIS_AUTOBACKUP_PERIOD_KEY = "backups:autobackup_period"

REDIS_AUTOBACKUP_QUOTAS_KEY = "backups:autobackup_quotas_key"

redis = RedisPool().get_connection()


class Storage:
    """Static class for storing backup related data in redis"""

    @staticmethod
    def reset() -> None:
        """Deletes all backup related data from redis"""
        redis.delete(REDIS_PROVIDER_KEY)
        redis.delete(REDIS_AUTOBACKUP_PERIOD_KEY)
        redis.delete(REDIS_INITTED_CACHE)
        redis.delete(REDIS_AUTOBACKUP_QUOTAS_KEY)

        prefixes_to_clean = [
            REDIS_SNAPSHOTS_PREFIX,
            REDIS_LAST_BACKUP_PREFIX,
        ]

        for prefix in prefixes_to_clean:
            for key in redis.keys(prefix + "*"):
                redis.delete(key)

    @staticmethod
    def invalidate_snapshot_storage() -> None:
        """Deletes all cached snapshots from redis"""
        for key in redis.keys(REDIS_SNAPSHOTS_PREFIX + "*"):
            redis.delete(key)

    @staticmethod
    def __last_backup_key(service_id: str) -> str:
        return REDIS_LAST_BACKUP_PREFIX + service_id

    @staticmethod
    def __snapshot_key(snapshot: Snapshot) -> str:
        return REDIS_SNAPSHOTS_PREFIX + snapshot.id

    @staticmethod
    def get_last_backup_time(service_id: str) -> Optional[datetime]:
        """Returns last backup time for a service or None if it was never backed up"""
        key = Storage.__last_backup_key(service_id)
        if not redis.exists(key):
            return None

        snapshot = hash_as_model(redis, key, Snapshot)
        if not snapshot:
            return None
        return snapshot.created_at

    @staticmethod
    def store_last_timestamp(service_id: str, snapshot: Snapshot) -> None:
        """Stores last backup time for a service"""
        store_model_as_hash(
            redis,
            Storage.__last_backup_key(service_id),
            snapshot,
        )

    @staticmethod
    def cache_snapshot(snapshot: Snapshot) -> None:
        """Stores snapshot metadata in redis for caching purposes"""
        snapshot_key = Storage.__snapshot_key(snapshot)
        store_model_as_hash(redis, snapshot_key, snapshot)

    @staticmethod
    def delete_cached_snapshot(snapshot: Snapshot) -> None:
        """Deletes snapshot metadata from redis"""
        snapshot_key = Storage.__snapshot_key(snapshot)
        redis.delete(snapshot_key)

    @staticmethod
    def get_cached_snapshot_by_id(snapshot_id: str) -> Optional[Snapshot]:
        """Returns cached snapshot by id or None if it doesn't exist"""
        key = REDIS_SNAPSHOTS_PREFIX + snapshot_id
        if not redis.exists(key):
            return None
        return hash_as_model(redis, key, Snapshot)

    @staticmethod
    def get_cached_snapshots() -> List[Snapshot]:
        """Returns all cached snapshots stored in redis"""
        keys: list[str] = redis.keys(REDIS_SNAPSHOTS_PREFIX + "*")  # type: ignore
        result: list[Snapshot] = []

        for key in keys:
            snapshot = hash_as_model(redis, key, Snapshot)
            if snapshot:
                result.append(snapshot)
        return result

    @staticmethod
    def autobackup_period_minutes() -> Optional[int]:
        """None means autobackup is disabled"""
        if not redis.exists(REDIS_AUTOBACKUP_PERIOD_KEY):
            return None
        return int(redis.get(REDIS_AUTOBACKUP_PERIOD_KEY))  # type: ignore

    @staticmethod
    def store_autobackup_period_minutes(minutes: int) -> None:
        """Set the new autobackup period in minutes"""
        redis.set(REDIS_AUTOBACKUP_PERIOD_KEY, minutes)

    @staticmethod
    def delete_backup_period() -> None:
        """Set the autobackup period to none, effectively disabling autobackup"""
        redis.delete(REDIS_AUTOBACKUP_PERIOD_KEY)

    @staticmethod
    def store_provider(provider: AbstractBackupProvider) -> None:
        """Stores backup provider auth data in redis"""
        model = BackupProviderModel(
            kind=get_kind(provider),
            login=provider.login,
            key=provider.key,
            location=provider.location,
            repo_id=provider.repo_id,
        )
        store_model_as_hash(redis, REDIS_PROVIDER_KEY, model)
        if Storage.load_provider() != model:
            raise IOError("could not store the provider model: ", model.dict)

    @staticmethod
    def load_provider() -> Optional[BackupProviderModel]:
        """Loads backup storage provider auth data from redis"""
        provider_model = hash_as_model(
            redis,
            REDIS_PROVIDER_KEY,
            BackupProviderModel,
        )
        return provider_model

    @staticmethod
    def has_init_mark() -> bool:
        """Returns True if the repository was initialized"""
        if redis.exists(REDIS_INITTED_CACHE):
            return True
        return False

    @staticmethod
    def mark_as_init():
        """Marks the repository as initialized"""
        redis.set(REDIS_INITTED_CACHE, 1)

    @staticmethod
    def mark_as_uninitted():
        """Marks the repository as initialized"""
        redis.delete(REDIS_INITTED_CACHE)

    @staticmethod
    def set_autobackup_quotas(quotas: AutobackupQuotas) -> None:
        store_model_as_hash(redis, REDIS_AUTOBACKUP_QUOTAS_KEY, quotas.to_pydantic())

    @staticmethod
    def autobackup_quotas() -> AutobackupQuotas:
        quotas_model = hash_as_model(
            redis, REDIS_AUTOBACKUP_QUOTAS_KEY, _AutobackupQuotas
        )
        if quotas_model is None:
            unlimited_quotas = AutobackupQuotas(
                last=-1,
                daily=-1,
                weekly=-1,
                monthly=-1,
                yearly=-1,
            )
            return unlimited_quotas
        return AutobackupQuotas.from_pydantic(quotas_model)  # pylint: disable=no-member