"""
On-demand values (cached when possible).

>>> get(Name.TEST_DUMMY)
'test value'
>>> reset(Name.TEST_DUMMY)
>>> get(Name.TEST_DUMMY)
'test value'
"""

import enum
import threading
import logging

import mini_buildd
import mini_buildd.cron

LOG = logging.getLogger(__name__)
LOCK = threading.Lock()


class Name(enum.Enum):
    TEST_DUMMY = enum.auto()
    DEFAULT_IDENTITY = enum.auto()
    DEFAULT_FTP_ENDPOINT = enum.auto()
    POWER_TOGGLE = enum.auto()
    ALL_REPOSITORIES = enum.auto()
    ALL_CHROOTS = enum.auto()
    ALL_REMOTES = enum.auto()
    ALL_CRONJOBS = enum.auto()
    PREPARED_DISTRIBUTIONS = enum.auto()
    ALL_DISTRIBUTIONS = enum.auto()
    ACTIVE_DISTRIBUTIONS = enum.auto()
    ACTIVE_UPLOADABLE_DISTRIBUTIONS = enum.auto()
    ACTIVE_EXPERIMENTAL_DISTRIBUTIONS = enum.auto()
    MIGRATABLE_DISTRIBUTIONS = enum.auto()
    ACTIVE_KEYRING_DISTRIBUTIONS = enum.auto()
    ALL_CODENAMES = enum.auto()
    ALL_SUITES = enum.auto()
    CURRENT_BUILDS = enum.auto()
    LAST_SOURCES = enum.auto()
    LAST_FAILED_BKEYS = enum.auto()
    DEFAULT_CHROOT_BACKEND = enum.auto()


class OnOff(enum.Enum):
    ON = True
    OFF = False


def _diststrs(repo_status, **suite_option_kwargs):
    diststrs = []
    for r in mini_buildd.mdls().repository.Repository.objects.filter(status__gte=repo_status):
        diststrs += r.mbd_distribution_strings(**suite_option_kwargs)
    return diststrs


def all_codenames():
    choices = []
    for r in mini_buildd.mdls().repository.Repository.objects.all():
        choices += list(r.mbd_icodenames())
    return set(choices)


SOURCES = {
    Name.TEST_DUMMY: {
        "cached": False,
        "get": lambda: "test value",
    },
    Name.DEFAULT_IDENTITY: {
        "cached": False,
        "get": mini_buildd.config.default_identity,
    },
    Name.DEFAULT_FTP_ENDPOINT: {
        "cached": False,
        "get": mini_buildd.config.default_ftp_endpoint,
    },
    Name.POWER_TOGGLE: {
        "cached": False,
        "get": lambda: OnOff(not mini_buildd.get_daemon().is_alive()).name,
    },
    Name.ALL_REPOSITORIES: {
        "cached": False,
        "get": lambda: [r.identity for r in mini_buildd.mdls().repository.Repository.objects.all()],
    },
    Name.ALL_CHROOTS: {
        "cached": False,
        "get": lambda: [c.mbd_key() for c in mini_buildd.mdls().chroot.Chroot.objects.all()],
    },
    Name.ALL_REMOTES: {
        "cached": False,
        "get": lambda: [r.http for r in mini_buildd.mdls().gnupg.Remote.objects.all()],
    },
    Name.ALL_CRONJOBS: {
        "cached": False,
        "get": lambda: {j.id() for j in mini_buildd.get_daemon().crontab.jobs}
    },
    Name.PREPARED_DISTRIBUTIONS: {
        "cached": False,
        "get": lambda: _diststrs(mini_buildd.mdls().repository.Repository.STATUS_PREPARED),
    },
    Name.ALL_DISTRIBUTIONS: {
        "cached": False,
        "get": lambda: _diststrs(mini_buildd.mdls().repository.Repository.STATUS_REMOVED),
    },
    Name.ACTIVE_DISTRIBUTIONS: {
        "cached": False,
        "get": lambda: _diststrs(mini_buildd.mdls().repository.Repository.STATUS_ACTIVE),
    },
    Name.ACTIVE_UPLOADABLE_DISTRIBUTIONS: {
        "cached": False,
        "get": lambda: _diststrs(mini_buildd.mdls().repository.Repository.STATUS_ACTIVE, uploadable=True),
    },
    Name.ACTIVE_EXPERIMENTAL_DISTRIBUTIONS: {
        "cached": False,
        "get": lambda: [d for d in _diststrs(mini_buildd.mdls().repository.Repository.STATUS_ACTIVE, experimental=True) if d.endswith("experimental")],
    },
    Name.MIGRATABLE_DISTRIBUTIONS: {
        "cached": False,
        "get": lambda: _diststrs(mini_buildd.mdls().repository.Repository.STATUS_PREPARED, migrates_to__isnull=False),
    },
    Name.ACTIVE_KEYRING_DISTRIBUTIONS: {
        "cached": False,
        "get": lambda: _diststrs(mini_buildd.mdls().repository.Repository.STATUS_ACTIVE, build_keyring_package=True),
    },
    Name.ALL_CODENAMES: {
        "cached": False,
        "get": all_codenames,
    },
    Name.ALL_SUITES: {
        "cached": False,
        "get": lambda: [s.name for s in mini_buildd.mdls().distribution.Suite.objects.all()],
    },
    Name.CURRENT_BUILDS: {
        "cached": False,
        "get": lambda: mini_buildd.get_daemon().builder.builds.keys(),   # pylint: disable=unnecessary-lambda  # lambda needed as we can't call mini_buildd.get_daemon() right here.
    },
    Name.LAST_SOURCES: {
        "cached": False,
        "get": lambda: mini_buildd.misc.attempt(lambda: sorted({event.source for event in mini_buildd.get_daemon().events if event.source}), retval_on_failure=[]),
    },
    Name.LAST_FAILED_BKEYS: {
        "cached": False,
        "get": lambda: mini_buildd.misc.attempt(lambda: sorted({event.extra.get("bkey") for event in mini_buildd.get_daemon().events if event.type == mini_buildd.events.Type.FAILED and event.extra}), retval_on_failure=[]),
    },
    Name.DEFAULT_CHROOT_BACKEND: {
        "cached": False,
        "get": lambda: "Dir",
    },
}
VALUES = {}


def get(name):
    with LOCK:
        value = VALUES.get(name)
        if value is None:
            value = SOURCES[name]["get"]()
        if SOURCES[name]["cached"]:
            VALUES[name] = value
        return value


def get_value(value):
    if isinstance(value, Name):
        return get(value)
    return value


def reset(name=None):
    with LOCK:
        for n in Name if name is None else [name]:
            VALUES[n] = None
            LOG.debug(f"Value reset: {name}")
