"""
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 os
import threading
import logging

from mini_buildd import config, util, events

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


class Name(enum.Enum):
    TEST_DUMMY = enum.auto()
    DEFAULT_IDENTITY = enum.auto()
    DEFAULT_FTP_ENDPOINT = 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()
    DEFAULT_DEBMIRROR_DESTINATION = enum.auto()


def _diststrs(repo_status, **suite_option_kwargs):
    diststrs = []
    for r in util.models().Repository.objects.filter(status__gte=repo_status):
        diststrs += r.mbd_get_diststrs(**suite_option_kwargs)
    return diststrs


def all_codenames():
    choices = []
    for r in util.models().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": config.default_identity,
    },
    Name.DEFAULT_FTP_ENDPOINT: {
        "cached": False,
        "get": config.default_ftp_endpoint,
    },
    Name.ALL_REPOSITORIES: {
        "cached": False,
        "get": lambda: [r.identity for r in util.models().Repository.objects.all()],
    },
    Name.ALL_CHROOTS: {
        "cached": False,
        "get": lambda: [c.mbd_key() for c in util.models().Chroot.objects.all()],
    },
    Name.ALL_REMOTES: {
        "cached": False,
        "get": lambda: [r.http for r in util.models().Remote.objects.all()],
    },
    Name.ALL_CRONJOBS: {
        "cached": False,
        "get": lambda: {j.id() for j in util.daemon().crontab.jobs}
    },
    Name.PREPARED_DISTRIBUTIONS: {
        "cached": False,
        "get": lambda: _diststrs(util.models().Repository.STATUS_PREPARED),
    },
    Name.ALL_DISTRIBUTIONS: {
        "cached": False,
        "get": lambda: _diststrs(util.models().Repository.STATUS_REMOVED),
    },
    Name.ACTIVE_DISTRIBUTIONS: {
        "cached": False,
        "get": lambda: _diststrs(util.models().Repository.STATUS_ACTIVE),
    },
    Name.ACTIVE_UPLOADABLE_DISTRIBUTIONS: {
        "cached": False,
        "get": lambda: _diststrs(util.models().Repository.STATUS_ACTIVE, uploadable=True),
    },
    Name.ACTIVE_EXPERIMENTAL_DISTRIBUTIONS: {
        "cached": False,
        "get": lambda: [d for d in _diststrs(util.models().Repository.STATUS_ACTIVE, experimental=True) if d.endswith("experimental")],
    },
    Name.MIGRATABLE_DISTRIBUTIONS: {
        "cached": False,
        "get": lambda: _diststrs(util.models().Repository.STATUS_PREPARED, migrates_to__isnull=False),
    },
    Name.ACTIVE_KEYRING_DISTRIBUTIONS: {
        "cached": False,
        "get": lambda: _diststrs(util.models().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 util.models().Suite.objects.all()],
    },
    Name.CURRENT_BUILDS: {
        "cached": False,
        "get": lambda: util.daemon().builder.builds.keys(),   # pylint: disable=unnecessary-lambda  # lambda needed as we can't call util.daemon() right here.
    },
    Name.LAST_SOURCES: {
        "cached": False,
        "get": lambda: util.attempt(lambda: sorted({event.source for event in util.daemon().events if event.source}), retval_on_failure=[]),
    },
    Name.LAST_FAILED_BKEYS: {
        "cached": False,
        "get": lambda: util.attempt(lambda: sorted({event.extra.get("bkey") for event in util.daemon().events if event.type == events.Type.FAILED and event.extra}), retval_on_failure=[]),
    },
    Name.DEFAULT_CHROOT_BACKEND: {
        "cached": False,
        "get": lambda: "Dir",
    },
    Name.DEFAULT_DEBMIRROR_DESTINATION: {
        "cached": False,
        "get": lambda: f"{os.path.expanduser('~')}/debmirror/{{}}",
    },
}
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}")
