"""
This module allows SPM to use the local filesystem to install files for SPM.

.. versionadded:: 2015.8.0
"""

import logging
import os
import os.path

import salt.syspaths
import salt.utils.files
import salt.utils.stringutils
import salt.utils.tarfileutil

# Get logging started
log = logging.getLogger(__name__)
FILE_TYPES = ("c", "d", "g", "l", "r", "s", "m")
# c: config file
# d: documentation file
# g: ghost file (i.e. the file contents are not included in the package payload)
# l: license file
# r: readme file
# s: SLS file
# m: Salt module


def init(**kwargs):
    """
    Initialize the directories for the files
    """
    formula_path = __opts__["formula_path"]
    pillar_path = __opts__["pillar_path"]
    reactor_path = __opts__["reactor_path"]
    for dir_ in (formula_path, pillar_path, reactor_path):
        if not os.path.exists(dir_):
            os.makedirs(dir_)
    return {
        "formula_path": formula_path,
        "pillar_path": pillar_path,
        "reactor_path": reactor_path,
    }


def check_existing(package, pkg_files, formula_def, conn=None):
    """
    Check the filesystem for existing files
    """
    if conn is None:
        conn = init()

    node_type = str(__opts__.get("spm_node_type"))

    existing_files = []
    for member in pkg_files:
        if member.isdir():
            continue

        tld = formula_def.get("top_level_dir", package)
        new_name = member.name.replace(f"{package}/", "")
        if not new_name.startswith(tld):
            continue

        if member.name.startswith(f"{package}/_"):
            if node_type in ("master", "minion"):
                # Module files are distributed via extmods directory
                out_file = os.path.join(
                    salt.syspaths.CACHE_DIR,
                    node_type,
                    "extmods",
                    new_name.replace("_", ""),
                )
            else:
                # Module files are distributed via _modules, _states, etc
                out_file = os.path.join(conn["formula_path"], new_name)
        elif member.name == f"{package}/pillar.example":
            # Pillars are automatically put in the pillar_path
            new_name = f"{package}.sls.orig"
            out_file = os.path.join(conn["pillar_path"], new_name)
        elif package.endswith("-conf"):
            # Configuration files go into /etc/salt/
            out_file = os.path.join(salt.syspaths.CONFIG_DIR, new_name)
        elif package.endswith("-reactor"):
            # Reactor files go into /srv/reactor/
            out_file = os.path.join(conn["reactor_path"], member.name)
        else:
            out_file = os.path.join(conn["formula_path"], member.name)

        if os.path.exists(out_file):
            existing_files.append(out_file)
            if not __opts__["force"]:
                log.error("%s already exists, not installing", out_file)

    return existing_files


def install_file(package, formula_tar, member, formula_def, conn=None):
    """
    Install a single file to the file system
    """
    if member.name == package:
        return False

    if conn is None:
        conn = init()

    node_type = str(__opts__.get("spm_node_type"))

    out_path = conn["formula_path"]

    tld = formula_def.get("top_level_dir", package)
    new_name = member.name.replace(f"{package}/", "", 1)
    if (
        not new_name.startswith(tld)
        and not new_name.startswith("_")
        and not new_name.startswith("pillar.example")
        and not new_name.startswith("README")
    ):
        log.debug("%s not in top level directory, not installing", new_name)
        return False

    for line in formula_def.get("files", []):
        tag = ""
        for ftype in FILE_TYPES:
            if line.startswith(f"{ftype}|"):
                tag = line.split("|", 1)[0]
                line = line.split("|", 1)[1]
        if tag and new_name == line:
            if tag in ("c", "d", "g", "l", "r"):
                out_path = __opts__["spm_share_dir"]
            elif tag in ("s", "m"):
                pass

    if member.name.startswith(f"{package}{os.sep}_"):
        if node_type in ("master", "minion"):
            # Module files are distributed via extmods directory
            member.name = new_name.replace(f"{package}{os.sep}_", "")
            out_path = os.path.join(
                salt.syspaths.CACHE_DIR,
                node_type,
                "extmods",
            )
        else:
            # Module files are distributed via _modules, _states, etc
            member.name = new_name.replace(f"{package}{os.sep}", "")
    elif member.name == f"{package}/pillar.example":
        # Pillars are automatically put in the pillar_path
        member.name = f"{package}.sls.orig"
        out_path = conn["pillar_path"]
    elif package.endswith("-conf"):
        # Configuration files go into /etc/salt/
        member.name = member.name.replace(f"{package}/", "")
        out_path = salt.syspaths.CONFIG_DIR
    elif package.endswith("-reactor"):
        # Reactor files go into /srv/reactor/
        out_path = __opts__["reactor_path"]

    # This ensures that double directories (i.e., apache/apache/) don't
    # get created
    comps = member.path.split("/")
    if len(comps) > 1 and comps[0] == comps[1]:
        member.path = "/".join(comps[1:])

    log.debug("Installing package file %s to %s", member.name, out_path)
    salt.utils.tarfileutil.extract(formula_tar, member, out_path)

    return out_path


def remove_file(path, conn=None):
    """
    Remove a single file from the file system
    """
    if conn is None:
        conn = init()

    log.debug("Removing package file %s", path)
    os.remove(path)


def hash_file(path, hashobj, conn=None):
    """
    Get the hexdigest hash value of a file
    """
    if os.path.isdir(path):
        return ""

    with salt.utils.files.fopen(path, "r") as f:
        hashobj.update(salt.utils.stringutils.to_bytes(f.read()))
        return hashobj.hexdigest()


def path_exists(path):
    """
    Check to see whether the file already exists
    """
    return os.path.exists(path)


def path_isdir(path):
    """
    Check to see whether the file already exists
    """
    return os.path.isdir(path)
