# Copyright (C) 2020  Braiins Systems s.r.o.
#
# This file is part of Braiins Open-Source Initiative (BOSI).
#
# BOSI is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.
#
# Please, keep in mind that we may also license BOSI or any part thereof
# under a proprietary license. For more information on the terms and conditions
# of such proprietary license or if you have any other questions, please
# contact us at opensource@braiins.com.

import gzip
import hashlib
import os
import shutil
import tarfile

from pathlib import Path
from tempfile import TemporaryDirectory
from urllib.request import Request, urlopen

from bos_toolbox.common import HOME_DIR


class WebCacheContext:
    def __init__(self, home_name=HOME_DIR):
        tmp_dir = TemporaryDirectory()
        home_dir = str(Path.home().joinpath(home_name))
        os.makedirs(home_dir, exist_ok=True)
        self.tmp_dir = tmp_dir
        self.cache = WebCache(tmp_dir.name, home_dir)

    def __enter__(self):
        return self.cache

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.tmp_dir.cleanup()


class WebCache:
    def __init__(self, tmp_dir, home_dir):
        self.tmp_dir = tmp_dir
        self.home_dir = home_dir

    @staticmethod
    def _hash_cache_name(cache_name: str):
        return hashlib.sha256(cache_name.encode()).hexdigest()[:8]

    def _get_tmp_cache_path(self, cache_name: str):
        cache_name_hash = self._hash_cache_name(cache_name)
        return os.path.join(self.tmp_dir, cache_name_hash)

    @staticmethod
    def _extract(cache_path, file_path, ungzip_name):
        os.mkdir(cache_path)
        if ungzip_name:
            ungzip_path = os.path.join(cache_path, ungzip_name)
            with gzip.open(file_path, 'rb') as f_in:
                with open(ungzip_path, 'wb') as f_out:
                    shutil.copyfileobj(f_in, f_out)
        else:
            tar = tarfile.open(file_path)
            tar.extractall(path=cache_path)
            tar.close()

    def download(self, cache_name: str, urlstring: str):
        file_path = os.path.join(self.home_dir, cache_name)
        if not os.path.isfile(file_path):
            # Download file from URL when it is not present in local cache
            with open(file_path, 'wb') as file:
                remote = urlopen(
                    Request(urlstring, headers={'User-Agent': 'Mozilla/5.0'})
                )
                shutil.copyfileobj(remote, file)
        return file_path

    def extract(self, cache_name: str, file_path, ungzip_name=None):
        cache_path = self._get_tmp_cache_path(cache_name)
        if not os.path.isdir(cache_path):
            self._extract(cache_path, file_path, ungzip_name)
        return cache_path

    def download_and_extract(self, cache_name: str, urlstring: str):
        cache_path = self._get_tmp_cache_path(cache_name)
        if not os.path.isdir(cache_path):
            file_path = self.download(cache_name, urlstring)
            self._extract(cache_path, file_path, None)
        return cache_path

    def remove_tmp_cache(self, cache_name: str):
        cache_path = self._get_tmp_cache_path(cache_name)
        shutil.rmtree(cache_path, ignore_errors=True)


if __name__ == '__main__':
    with WebCacheContext('.bos-toolbox') as web_cache:
        cache_name = 'braiins-os_am1-s9_ssh_2020-10-25-0-908ca41d-20.10-plus.tar.gz'
        urlstring = 'https://feeds.braiins-os.com/20.10/' + cache_name
        firmware = web_cache.download_and_extract(cache_name, urlstring)
        print('{}'.format(firmware))
        firmware = web_cache.download_and_extract(cache_name, urlstring)
        print('{}'.format(firmware))

        cache_name = 'Antminer-S17-user-OM-202004271323-sig_5835.tar.gz'
        urlstring = (
            'https://file12.bitmain.com/shop-product/firmware/62c2b853-b65e-4dd6-98c3-b61960e9d4d3/2020/06/30/18/'
            + cache_name
        )
        firmware = web_cache.download_and_extract(cache_name, urlstring)
        print('{}'.format(firmware))
        firmware = web_cache.download_and_extract(cache_name, urlstring)
        print('{}'.format(firmware))
