import os
import tempfile

import pytest

from snakeoil import chksum, fileutils
from snakeoil.currying import post_curry
from snakeoil.data_source import data_source, local_source

data = "afsd123klawerponzzbnzsdf;h89y23746123;haas"
multi = 40000


def require_chf(func):
    def subfunc(self):
        if self.chf is None:
            pytest.skip(
                "no handler for %s, do you need to install PyCrypto or mhash?"
                % (self.chf_type,)
            )
        func(self)

    return subfunc


class base:
    def get_chf(self):
        try:
            self.chf = chksum.get_handler(self.chf_type)
        except chksum.MissingChksumHandler:
            self.chf = None

    def setup_method(self, method):
        self.get_chf()
        fd, self.fn = tempfile.mkstemp()
        for i in range(multi):
            os.write(fd, data.encode())
        os.close(fd)

    def teardown_method(self, method):
        try:
            os.unlink(self.fn)
        except IOError:
            pass

    @require_chf
    def test_fp_check(self):
        assert self.chf(self.fn) == self.expected_long

    @require_chf
    def test_fileobj_check(self):
        with open(self.fn, "r") as f:
            assert self.chf(f) == self.expected_long

    @require_chf
    def test_data_source_check(self):
        assert self.chf(local_source(self.fn)) == self.expected_long
        assert (
            self.chf(data_source(fileutils.readfile_ascii(self.fn)))
            == self.expected_long
        )


class ChksumTest(base):
    @require_chf
    def test_str2long(self):
        assert self.chf.str2long(self.expected_str) == self.expected_long
        if self.chf_type == "size":
            return
        for x in extra_chksums.get(self.chf_type, ()):
            assert self.chf.str2long(x) == int(x, 16)

    @require_chf
    def test_long2str(self):
        assert self.chf.long2str(self.expected_long) == self.expected_str
        if self.chf_type == "size":
            return
        for x in extra_chksums.get(self.chf_type, ()):
            assert self.chf.long2str(int(x == 16)), x


checksums = {
    "rmd160": "b83ad488d624e7911f886420ab230f78f6368b9f",
    "sha1": "63cd8cce8a1773dffb400ee184be3ec7d89791f5",
    "md5": "d17ea153bc57ba9e07298c5378664369",
    "sha256": "68ae37b45e4a4a5df252db33c0cbf79baf5916b5ff6fc15e8159163b6dbe3bae",
    "sha512": "cdc2b749d28cd9c5fca45d3ca6b65661445decd992da93054fd6f4f3e4013ca8b44b0ba159d1cf1f58f9af2b9d267343b9e10f611494c0850fdcebe0379135c6",
    "sha3_256": "33e910cd302a2a210ccd9bc5331d61c164aa228a23af2ae97edc9eb60ddb01f9",
    "sha3_512": "820e3526c76ca2c41582439c50b395aef7560ae57c5d6273fe7dbaa01f4f1f121ddbb147cf42fc23e0a2823bf0bb4c47027cd35620141126d374c6782a512f95",
    "blake2b": "9f9bbd37d28994c871fffbc21358358e79c85c80fad70a0c0ce5998ff9ff04001f4984ec46e596bd4c482adc701cca44f70318c389dc6014c1bb5818d6991c7f",
    "blake2s": "805b836cb59b5144b2a738422b342a90fbdc0dd8e75321eb3022766ff333a7b1",
}
checksums.update((k, (int(v, 16), v)) for k, v in checksums.items())
checksums["size"] = (int(len(data) * multi), str(int(len(data) * multi)))

extra_chksums = {"md5": ["2dfd84279314a178d0fa842af3a40e25577e1bc"]}

for k, v in checksums.items():
    extra_chksums.setdefault(k, []).extend(
        ("".rjust(len(v[1]), "0"), "01".rjust(len(v[1]), "0"))
    )

# trick: create subclasses for each checksum with a useful class name.
for chf_type, expected in checksums.items():
    expectedsum = expected[0]
    expectedstr = expected[1]
    globals()["TestChksum" + chf_type.capitalize()] = type(
        "TestChksum" + chf_type.capitalize(),
        (ChksumTest,),
        dict(chf_type=chf_type, expected_long=expectedsum, expected_str=expectedstr),
    )

# pylint: disable=undefined-loop-variable
del chf_type, expected


class TestGetChksums(base):
    chfs = [k for k in sorted(checksums) if k in ("md5", "sha1")]
    expected_long = [checksums[k][0] for k in chfs]

    def get_chf(self):
        self.chf = post_curry(chksum.get_chksums, *self.chfs)
