"""The 'new' conda format, introduced in late 2018/early 2019.  Spec at
https://anaconda.atlassian.net/wiki/spaces/AD/pages/90210540/Conda+package+format+v2"""

import json
import logging
import os
import shutil
import zipfile

from conda_package_handling import utils
from conda_package_handling.interface import AbstractBaseFormat
from conda_package_handling.tarball import create_compressed_tarball, _tar_xf
from conda_package_handling.utils import TemporaryDirectory

CONDA_PACKAGE_FORMAT_VERSION = 2
DEFAULT_COMPRESSION_TUPLE = ('.tar.zst', 'zstd', 'zstd:compression-level=22')


def _write_conda_pkg_version_spec(tmpdir):
    metadata_file = os.path.join(tmpdir, 'metadata.json')
    pkg_metadata = {'conda_pkg_format_version': CONDA_PACKAGE_FORMAT_VERSION}
    with open(metadata_file, 'w') as f:
        json.dump(pkg_metadata, f)


def _lookup_component_filename(zf, file_id, component_name):
    contents = zf.namelist()
    component_filename_without_ext = '-'.join((component_name, file_id))
    component_filename = [_ for _ in contents if
                            _.startswith(component_filename_without_ext)]
    return component_filename


def _extract_component(fn, file_id, component_name, dest_dir=None):
    with TemporaryDirectory() as tmp:
        with utils.tmp_chdir(tmp):
            with zipfile.ZipFile(fn, compression=zipfile.ZIP_STORED) as zf:
                component_filename = _lookup_component_filename(zf, file_id, component_name)
                if not component_filename:
                    raise RuntimeError("didn't find {} component in {}".format(component_name, fn))
                component_filename = component_filename[0]
                zf.extract(component_filename)
                _tar_xf(component_filename, dest_dir)


class CondaFormat_v2(AbstractBaseFormat):
    """If there's another conda format or breaking changes, please create a new class and keep this
    one, so that handling of v2 stays working."""

    @staticmethod
    def extract(fn, dest_dir, **kw):
        components = utils.ensure_list(kw.get('components')) or ('info', 'pkg')
        file_id = os.path.basename(fn).replace('.conda', '')
        if not os.path.isabs(fn):
            fn = os.path.normpath(os.path.join(os.getcwd(), fn))
        if not os.path.isdir(dest_dir):
            os.makedirs(dest_dir)
        for component in components:
            _extract_component(fn, file_id, component, dest_dir)

    @staticmethod
    def extract_info(fn, dest_dir=None):
        return CondaFormat_v2.extract(fn, dest_dir, components=['info'])

    @staticmethod
    def create(prefix, file_list, out_fn, out_folder=os.getcwd(), **kw):
        with TemporaryDirectory() as tmp:
            out_fn = out_fn.replace('.conda', '')
            conda_pkg_fn = os.path.join(tmp, out_fn) + '.conda'
            pkg_files = utils.filter_info_files(file_list, prefix)
            info_files = set(file_list) - set(pkg_files)
            ext, comp_filter, filter_opts = kw.get('compression_tuple') or DEFAULT_COMPRESSION_TUPLE

            info_tarball = create_compressed_tarball(prefix, info_files, tmp, 'info-' + out_fn,
                                                    ext, comp_filter, filter_opts)
            pkg_tarball = create_compressed_tarball(prefix, pkg_files, tmp, 'pkg-' + out_fn,
                                                    ext, comp_filter, filter_opts)

            _write_conda_pkg_version_spec(tmp)

            with utils.tmp_chdir(tmp):
                with zipfile.ZipFile(conda_pkg_fn, 'w', compression=zipfile.ZIP_STORED) as zf:
                    for pkg in (info_tarball, pkg_tarball):
                        zf.write(os.path.basename(pkg))
                    zf.write('metadata.json')
            final_path = os.path.join(out_folder, os.path.basename(conda_pkg_fn))
            try:
                shutil.copy(conda_pkg_fn, final_path)
            except OSError as e:
                logging.getLogger(__name__).info("Copying temporary"
                    "package from {} to {} had some issues.  Error "
                    "message was: {}".format(conda_pkg_fn, final_path, repr(e)))
        return final_path

    @staticmethod
    def get_pkg_details(in_file):
        stat_result = os.stat(in_file)
        size = stat_result.st_size
        # open the file twice because we need to start from the beginning each time
        with open(in_file, 'rb') as f:
            md5 = utils.md5_checksum(f)
        with open(in_file, 'rb') as f:
            sha256 = utils.sha256_checksum(f)
        return {"size": size, "md5": md5, "sha256": sha256}
