"""
unit tests for the pillar runner
"""

import logging
import os
import shutil

import pytest

import salt.runners.pillar as pillar_runner
import salt.utils.files
import salt.utils.msgpack
from tests.support.mock import MagicMock, mock_open, patch

log = logging.getLogger(__name__)


@pytest.fixture
def configure_loader_modules(master_opts):
    master_opts.update(
        {
            "pillar_cache": True,
            "pillar_cache_backend": "disk",
            "pillar_cache_ttl": 30,
            "state_top": "top.sls",
            "pillar_roots": [],
            "fileserver_backend": [],
            "keys.cache_driver": "localfs_key",
            "__role": "master",
        }
    )
    return {pillar_runner: {"__opts__": master_opts}}


@pytest.fixture(scope="module")
def cachedir_tree(tmp_path_factory):
    _cachedir_tree = tmp_path_factory.mktemp("cachedir")
    try:
        yield _cachedir_tree
    finally:
        shutil.rmtree(str(_cachedir_tree), ignore_errors=True)


@pytest.fixture(scope="module")
def pillar_cache_dir(cachedir_tree):
    pillar_cache_dir = cachedir_tree / "pillar"
    pillar_cache_dir.mkdir()
    return pillar_cache_dir


@pytest.fixture(scope="function")
def pillar_cache_files(pillar_cache_dir):
    MINION_ID = "test-host"
    pillar = {
        "this": "one",
        "that": "two",
        "those": ["three", "four", "five"],
    }
    packer = salt.utils.msgpack.Packer()
    cache_contents = packer.pack(pillar)
    cache_file = os.path.join(str(pillar_cache_dir), f"{MINION_ID}.p")

    with salt.utils.files.fopen(cache_file, "wb+") as fp:
        fp.write(cache_contents)
    mtime = 1612302460.146923
    os.utime(cache_file, (mtime, mtime))

    MINION_ID = "another-host"
    cache = {
        "this": "six",
        "that": "seven",
        "those": ["eight", "nine", "ten"],
    }
    packer = salt.utils.msgpack.Packer()
    cache_contents = packer.pack(cache)

    cache_file = os.path.join(str(pillar_cache_dir), f"{MINION_ID}.p")
    with salt.utils.files.fopen(cache_file, "wb+") as fp:
        fp.write(cache_contents)
    mtime = 1612302460.146923
    os.utime(cache_file, (mtime, mtime))


def test_clear_pillar_cache(cachedir_tree, pillar_cache_dir, pillar_cache_files):
    """
    test pillar.clear_pillar_cache
    """

    MINION_IDS = [
        ["test-host", "another-host"],
        ["test-host"],
        ["test-host", "another-host"],
        ["test-host", "another-host"],
    ]
    _CHECK_MINIONS_RETURN = []
    for entry in MINION_IDS:
        _CHECK_MINIONS_RETURN.append({"minions": entry, "missing": []})

    with patch.dict(pillar_runner.__opts__, {"cachedir": str(cachedir_tree)}):
        with patch(
            "salt.utils.minions.CkMinions.check_minions",
            MagicMock(side_effect=_CHECK_MINIONS_RETURN),
        ):
            with patch("salt.pillar.Pillar._Pillar__gather_avail", return_value={}):
                expected = {
                    "test-host": {
                        "those": ["three", "four", "five"],
                        "that": "two",
                        "this": "one",
                    },
                    "another-host": {
                        "those": ["eight", "nine", "ten"],
                        "that": "seven",
                        "this": "six",
                    },
                }
                ret = pillar_runner.show_pillar_cache()
                assert ret == expected

                ret = pillar_runner.clear_pillar_cache("test-host")
                assert ret is True

                expected = {
                    "another-host": {
                        "those": ["eight", "nine", "ten"],
                        "that": "seven",
                        "this": "six",
                    }
                }
                ret = pillar_runner.show_pillar_cache()
                assert ret == expected

                ret = pillar_runner.clear_pillar_cache()
                assert ret is True


def test_show_pillar_cache(cachedir_tree, pillar_cache_dir, pillar_cache_files):
    """
    test pillar.clear_pillar_cache
    """

    MINION_IDS = [["test-host", "another-host"], ["test-host"]]

    _CHECK_MINIONS_RETURN = []
    for entry in MINION_IDS:
        _CHECK_MINIONS_RETURN.append({"minions": entry, "missing": []})

    with patch.dict(pillar_runner.__opts__, {"cachedir": str(cachedir_tree)}):
        with patch(
            "salt.utils.minions.CkMinions.check_minions",
            MagicMock(side_effect=_CHECK_MINIONS_RETURN),
        ):
            with patch("salt.pillar.Pillar._Pillar__gather_avail", return_value={}):
                with patch(
                    "salt.utils.minions.CkMinions.check_minions",
                    MagicMock(side_effect=_CHECK_MINIONS_RETURN),
                ):
                    expected = {
                        "test-host": {
                            "those": ["three", "four", "five"],
                            "that": "two",
                            "this": "one",
                        },
                        "another-host": {
                            "those": ["eight", "nine", "ten"],
                            "that": "seven",
                            "this": "six",
                        },
                    }
                    ret = pillar_runner.show_pillar_cache()
                    assert ret == expected

                    expected = {
                        "test-host": {
                            "this": "one",
                            "that": "two",
                            "those": ["three", "four", "five"],
                        }
                    }
                    ret = pillar_runner.show_pillar_cache("test-host")
                    assert ret == expected

                _EMPTY_CHECK_MINIONS_RETURN = {"minions": [], "missing": []}
                with patch(
                    "salt.utils.minions.CkMinions.check_minions",
                    MagicMock(return_value=_EMPTY_CHECK_MINIONS_RETURN),
                ), patch(
                    "salt.utils.atomicfile.atomic_open", mock_open()
                ) as atomic_open_mock:
                    ret = pillar_runner.show_pillar_cache("fake-host")
                    assert ret == {}
