import os
import pickle
from io import BytesIO

import pytest

from sigal.gallery import Gallery
from sigal.plugins.encrypt import endec
from sigal.plugins.encrypt.encrypt import cache_key
from sigal.utils import init_plugins

CURRENT_DIR = os.path.dirname(__file__)


def get_key_tag(settings):
    options = settings["encrypt_options"]
    key = endec.kdf_gen_key(
        options["password"], options["kdf_salt"], options["kdf_iters"]
    )
    tag = options["gcm_tag"].encode("utf-8")
    return (key, tag)


def test_encrypt(settings, tmpdir, disconnect_signals, caplog):
    settings["source"] = os.path.join(settings["source"], "encryptTest")
    settings["destination"] = str(tmpdir)
    settings["plugins"] = ["sigal.plugins.encrypt"]

    init_plugins(settings)
    gal = Gallery(settings, ncpu=1)

    with pytest.raises(ValueError, match="no encrypt_options in settings"):
        gal.build()

    settings["encrypt_options"] = {}

    gal = Gallery(settings, ncpu=1)

    with pytest.raises(ValueError, match="no password provided"):
        gal.build()

    settings["encrypt_options"] = {
        "password": "password",
        "ask_password": True,
        "encrypt_symlinked_originals": False,
    }

    gal = Gallery(settings, ncpu=1)
    gal.build()

    # check the encrypt cache exists
    cachePath = os.path.join(settings["destination"], ".encryptCache")
    assert os.path.isfile(cachePath)

    encryptCache = None
    with open(cachePath, "rb") as cacheFile:
        encryptCache = pickle.load(cacheFile)
    assert isinstance(encryptCache, dict)

    testAlbum = gal.albums["."]
    key, tag = get_key_tag(settings)

    for media in testAlbum:
        # check if sizes are stored in cache
        assert cache_key(media) in encryptCache
        assert "size" in encryptCache[cache_key(media)]
        assert "thumb_size" in encryptCache[cache_key(media)]
        assert "encrypted" in encryptCache[cache_key(media)]

        encryptedImages = [media.dst_path, media.thumb_path]
        if settings["keep_orig"]:
            encryptedImages.append(
                os.path.join(settings["destination"], media.path, media.big)
            )

        # check if images are encrypted by trying to decrypt
        for image in encryptedImages:
            with open(image, "rb") as infile:
                with BytesIO() as outfile:
                    endec.decrypt(key, infile, outfile, tag)

    # check static files have been copied
    static = os.path.join(settings["destination"], "static")
    assert os.path.isfile(os.path.join(static, "decrypt.js"))
    assert os.path.isfile(os.path.join(static, "keycheck.txt"))
    assert os.path.isfile(os.path.join(settings["destination"], "sw.js"))

    # check keycheck file
    with open(
        os.path.join(settings["destination"], "static", "keycheck.txt"), "rb"
    ) as infile:
        with BytesIO() as outfile:
            endec.decrypt(key, infile, outfile, tag)

    caplog.clear()
    caplog.set_level("DEBUG")
    gal = Gallery(settings, ncpu=1)
    gal.build()
    # Doesn't work on Actions ...
    # assert 'Loaded cache with 34 entries' in caplog.messages
    # assert 'Loaded encryption cache with 27 entries' in caplog.messages
