#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.
#
import os
import re
import shutil
import subprocess
import sys

import pytest


SETUP_TEMPLATE = """\
from setuptools import setup

setup(
    name='{name}',
    version='{version}',
    description='{name}',
    packages=['{pkgdirname}'],
    install_requires={pkgdeps},
    entry_points={{
        'console_scripts': [
            '{pkgdirname}={pkgdirname}:main'
        ]
    }}
)
"""

# All packages generated via generate_pip_package will have the functions below
INIT_TEMPLATE = """\
def main():
    print('This is {name}')

def hello(actor='world'):
    print('Hello {{}}! This is {name}'.format(actor))
"""

HTML_TEMPLATE = """\
<html>
  <head>
    <title>Links for {name}</title>
  </head>
  <body>
    <a href='{name}-{version}.tar.gz'>{name}-{version}.tar.gz</a><br />
  </body>
</html>
"""


# Creates a simple python source distribution and copies this into a specified
# directory which is to serve as a mock python repository
#
# Args:
#    tmpdir (str): Directory in which the source files will be created
#    pypi (str): Directory serving as a mock python repository
#    name (str): The name of the package to be created
#    version (str): The version of the package to be created
#
# Returns:
#    None
#
def generate_pip_package(tmpdir, pypi, name, version="0.1", dependencies=None):
    if dependencies is None:
        dependencies = []
    # check if package already exists in pypi
    pypi_package = os.path.join(pypi, re.sub("[^0-9a-zA-Z]+", "-", name))
    if os.path.exists(pypi_package):
        return

    # create the package source files in tmpdir resulting in a directory
    # tree resembling the following structure:
    #
    # tmpdir
    # |-- setup.py
    # `-- package
    #     `-- __init__.py
    #
    setup_file = os.path.join(tmpdir, "setup.py")
    pkgdirname = re.sub("[^0-9a-zA-Z]+", "", name)
    with open(setup_file, "w", encoding="utf-8") as f:
        f.write(SETUP_TEMPLATE.format(name=name, version=version, pkgdirname=pkgdirname, pkgdeps=dependencies))
    os.chmod(setup_file, 0o755)

    package = os.path.join(tmpdir, pkgdirname)
    os.makedirs(package)

    main_file = os.path.join(package, "__init__.py")
    with open(main_file, "w", encoding="utf-8") as f:
        f.write(INIT_TEMPLATE.format(name=name))
    os.chmod(main_file, 0o644)

    # Run sdist with a fresh process
    subprocess.run([sys.executable, "setup.py", "sdist"], cwd=tmpdir, check=True)

    # create directory for this package in pypi resulting in a directory
    # tree resembling the following structure:
    #
    # pypi
    # `-- pypi_package
    #     |-- index.html
    #     `-- foo-0.1.tar.gz
    #
    os.makedirs(pypi_package)

    # add an index html page
    index_html = os.path.join(pypi_package, "index.html")
    with open(index_html, "w", encoding="utf-8") as f:
        f.write(HTML_TEMPLATE.format(name=name, version=version))

    # copy generated tarfile to pypi package
    dist_dir = os.path.join(tmpdir, "dist")
    for tar in os.listdir(dist_dir):
        tarpath = os.path.join(dist_dir, tar)
        shutil.copy(tarpath, pypi_package)


@pytest.fixture
def setup_pypi_repo(tmpdir):
    def create_pkgdir(package):
        pkgdirname = re.sub("[^0-9a-zA-Z]+", "", package)
        pkgdir = os.path.join(str(tmpdir), pkgdirname)
        os.makedirs(pkgdir)
        return pkgdir

    def add_packages(packages, pypi_repo):
        for package, dependencies in packages.items():
            pkgdir = create_pkgdir(package)
            generate_pip_package(pkgdir, pypi_repo, package, dependencies=list(dependencies.keys()))
            for dependency, dependency_dependencies in dependencies.items():
                add_packages({dependency: dependency_dependencies}, pypi_repo)

    return add_packages
