# Copyright (c) 2018 The Pooch Developers.
# Distributed under the terms of the BSD 3-Clause License.
# SPDX-License-Identifier: BSD-3-Clause
#
# This code is part of the Fatiando a Terra project (https://www.fatiando.org)
#
"""
Utilities for testing code.
"""
import os
import io
import logging
import shutil
import stat
from pathlib import Path
from contextlib import contextmanager

from .. import __version__ as full_version
from ..utils import check_version, get_logger


def check_tiny_data(fname):
    """
    Load the tiny-data.txt file and check that the contents are correct.
    """
    assert os.path.exists(fname)
    with open(fname) as tinydata:
        content = tinydata.read()
    true_content = "\n".join(
        ["# A tiny data file for test purposes only", "1  2  3  4  5  6"]
    )
    assert content.strip() == true_content


def check_large_data(fname):
    """
    Load the large-data.txt file and check that the contents are correct.
    """
    assert os.path.exists(fname)
    with open(fname) as data:
        content = data.read()
    true_content = ["# A larer data file for test purposes only"]
    true_content.extend(["1  2  3  4  5  6"] * 6002)
    assert content.strip() == "\n".join(true_content)


def pooch_test_url():
    """
    Get the base URL for the test data used in Pooch itself.

    The URL is a GitHub raw link to the ``pooch/tests/data`` directory from the
    `GitHub repository <https://github.com/fatiando/pooch>`__. It matches the
    pooch version specified in ``pooch.version.full_version``.

    Returns
    -------
    url
        The versioned URL for pooch's test data.

    """
    url = "https://github.com/fatiando/pooch/raw/{version}/pooch/tests/data/".format(
        version=check_version(full_version, fallback="main")
    )
    return url


def pooch_test_figshare_url():
    """
    Get the base URL for the test data stored in figshare.

    The URL contains the DOI for the figshare dataset using the appropriate
    version for this version of Pooch.

    Returns
    -------
    url
        The URL for pooch's test data.

    """
    url = "doi:10.6084/m9.figshare.14763051.v1/"
    return url


def pooch_test_zenodo_url():
    """
    Get the base URL for the test data stored in Zenodo.

    The URL contains the DOI for the Zenodo dataset using the appropriate
    version for this version of Pooch.

    Returns
    -------
    url
        The URL for pooch's test data.

    """
    url = "doi:10.5281/zenodo.4924875/"
    return url


def pooch_test_zenodo_with_slash_url():
    """
    Get base URL for test data in Zenodo, where the file name contains a slash

    The URL contains the DOI for the Zenodo dataset that has a slash in the
    filename (created with the GitHub-Zenodo integration service), using the
    appropriate version for this version of Pooch.

    Returns
    -------
    url
        The URL for pooch's test data.

    """
    url = "doi:10.5281/zenodo.7632643/"
    return url


def pooch_test_dataverse_url():
    """
    Get the base URL for the test data stored on a DataVerse instance.

    Returns
    -------
    url
        The URL for pooch's test data.
    """
    url = "doi:10.11588/data/TKCFEF/"
    return url


def pooch_test_registry():
    """
    Get a registry for the test data used in Pooch itself.

    Returns
    -------
    registry
        Dictionary with pooch's test data files and their hashes.

    """
    registry = {
        "tiny-data.txt": "baee0894dba14b12085eacb204284b97e362f4f3e5a5807693cc90ef415c1b2d",
        "large-data.txt": "98de171fb320da82982e6bf0f3994189fff4b42b23328769afce12bdd340444a",
        "subdir/tiny-data.txt": "baee0894dba14b12085eacb204284b97e362f4f3e5a5807693cc90ef415c1b2d",
        "tiny-data.zip": "0d49e94f07bc1866ec57e7fd1b93a351fba36842ec9b13dd50bf94e8dfa35cbb",
        "store.zip": "0498d2a001e71051bbd2acd2346f38da7cbd345a633cb7bf0f8a20938714b51a",
        "tiny-data.tar.gz": "41503f083814f43a01a8e9a30c28d7a9fe96839a99727a7fdd0acf7cd5bab63b",
        "store.tar.gz": "088c7f4e0f1859b1c769bb6065de24376f366374817ede8691a6ac2e49f29511",
        "tiny-data.txt.bz2": "753663687a4040c90c8578061867d1df623e6aa8011c870a5dbd88ee3c82e306",
        "tiny-data.txt.gz": "2e2da6161291657617c32192dba95635706af80c6e7335750812907b58fd4b52",
        "tiny-data.txt.xz": "99dcb5c32a6e916344bacb4badcbc2f2b6ee196977d1d8187610c21e7e607765",
    }
    return registry


@contextmanager
def capture_log(level=logging.DEBUG):
    """
    Create a context manager for reading from the logs.

    Yields
    ------
    log_file : StringIO
        a file-like object to which the logs were written
    """
    log_file = io.StringIO()
    handler = logging.StreamHandler(log_file)
    handler.setLevel(level)
    get_logger().addHandler(handler)
    yield log_file
    get_logger().removeHandler(handler)


@contextmanager
def data_over_ftp(server, fname):
    """
    Add a test data file to the test FTP server and clean it up afterwards.

    Parameters
    ----------
    server
        The ``ftpserver`` fixture provided by pytest-localftpserver.
    fname : str
        The name of a file *relative* to the test data folder of the package
        (usually just the file name, not the full path).

    Yields
    ------
    url : str
        The download URL of the data file from the test FTP server.

    """
    package_path = str(Path(__file__).parent / "data" / fname)
    server_path = os.path.join(server.anon_root, fname)
    try:
        shutil.copyfile(package_path, server_path)
        url = f"ftp://localhost/{fname}"
        yield url
    finally:
        if os.path.exists(server_path):
            os.remove(server_path)


def _recursive_chmod_directories(root, mode):
    """
    Recursively change the permissions on the child directories using a bitwise
    OR operation.
    """
    for item in root.iterdir():
        if item.is_dir():
            item.chmod(item.stat().st_mode | mode)
            _recursive_chmod_directories(item, mode)


def mirror_directory(source, destination):
    """
    Copy contents of the source directory into destination and fix permissions.

    Parameters
    ----------
    source : str, :class:`pathlib.Path`
        Source data directory.
    destination : str, :class:`pathlib.Path`
        Destination directory that will contain the copy of source. The actual
        source directory (not just it's contents) is copied.

    Returns
    -------
    mirror : :class:`pathlib.Path`
        The path of the mirrored output directory.

    """
    source = Path(source)
    mirror = Path(destination) / source.name
    shutil.copytree(source, mirror)
    _recursive_chmod_directories(mirror, mode=stat.S_IWUSR)
    return mirror
