# Copyright (c) 2010 ActiveState Software Inc. All rights reserved.
"""
    pypm.client.hooks
    ~~~~~~~~~~~~~~~~~

    Built-in set of install/uninstall hooks
"""

from __future__ import unicode_literals

import os
import os.path as P
import logging

import six
from applib import sh

from pypm.common.util import concise_path
from pypm.client.base import ImagePythonEnvironment


if not six.PY3:
    from codecs import open

LOG = logging.getLogger(__name__)

__all__ = ['POSTINSTALL', 'PREUNINSTALL']


def postinstall_generic(pypmenv, ipkg):
    """Run the generic postinstall script (if exists)

    The postinstall script is expected to be found in the additional files area:
    share/<name>/postinstall.py.
    """
    # Use forward slash, as that is ipkg.files_lists uses as well
    postinstall_script = 'share/{0}/postinstall.py'.format(ipkg.name)
    if postinstall_script not in ipkg.files_list:
        return
    postinstall_script = P.join(pypmenv.pyenv.base_dir, postinstall_script)
    assert P.exists(postinstall_script)
    
    LOG.debug('Running postinstall: %s', postinstall_script)
    try:
        stdout, _ = sh.run(
            [pypmenv.pyenv.python_exe, postinstall_script],
            merge_streams=True)
    except sh.RunError as e:
        LOG.error('** postinstall script failed **; '
                  'run `pypm log` for details')
        LOG.debug('postinstall: %s', str(e).strip())
    else:
        if stdout:
            LOG.info('postinstall: %s', stdout.strip())


def postinstall_notes(pypmenv, ipkg):
    """Hook to show package-specific notes to the user

    Show notes appropriate to the just-installed package
    """
    in_section = False
    for note in ipkg.get_notes(postinstall=True):
        if not in_section:
            LOG.info('*' * 40)
            in_section = True
        LOG.info(note['content'].strip())
    if in_section:
        LOG.info('*' * 40)


def postinstall_fix_shebang(pypmenv, ipkg):
    """Fix #! hardcoding in scripts

    While running ``easy_install $pkg.name`` is `one solution`_, it will not
    work with packages that use only distutils (not setuptools).

    Note: On Windows, foo.exe uses the shebang line from foo-script.py; so
    we should run this fixer on Windows too.

    .. _`one solution`:
    http://mail.python.org/pipermail/distutils-sig/2008-April/009283.html
    """
    assert os.path.exists(pypmenv.pyenv.python_exe)
    pyenv = pypmenv.pyenv
    if isinstance(pyenv, ImagePythonEnvironment):
        python_exe = pyenv.target_python_exe
        LOG.info('using ImagePythonEnvironment w/ target=%s', python_exe)
    else:
        python_exe = pyenv.python_exe

    scripts_dir = os.path.relpath(
        pyenv.get_install_scheme_path('scripts'),
        pyenv.base_dir)
    binary_exts = ('.exe', '.dll', '.bin', '.dat')

    for path in ipkg.files_list:
        if path.startswith(scripts_dir) and \
           path != scripts_dir and \
           not path.lower().endswith(binary_exts):
            _fix_script(pyenv.get_abspath(path), python_exe)
        else:
            # Just a sanity check to make sure that we didn't break our
            # earlier behaviour (<=2.6.5.13) by using `get_install_scheme_path`
            assert not all([any([path.startswith('bin/'),
                                 path.startswith('Scripts/')]),
                            not path.lower().endswith(binary_exts)]), \
                    "'%s' seems like a script, but does not belong to " \
                    "your the scripts install scheme (%s)" % (
                        path, scripts_dir)


def _fix_script(script_path, python_exe):
    """replace old #! path with ``python_exe``"""
    if P.islink(script_path):
        # ignore symlinks (including broken ones)
        return
    # read in 'byte' mode and see if the first two chars are #!; if not
    # return immediately to avoid failing with binary files.
    with open(script_path, 'rb') as file:
        if file.read(2) != b'#!':
            return

    with open(script_path, encoding='utf-8') as file:
        content = file.read()

    if not content:
        return

    first_line = content.splitlines()[0].strip()
    assert first_line.startswith('#!')
    # HACK: guess Python shebang (apart from, say, /bin/sh)
    if 'python' in first_line.lower():
        shebang = '#!' + python_exe
        LOG.info('Fixing script %s', concise_path(script_path))
        LOG.debug('Replacing shebang "%s" with "%s"',
                  first_line, shebang)
        content = content.replace(first_line, shebang)
        with open(script_path, 'w', encoding='utf-8') as file:
            file.write(content)



POSTINSTALL = [
    postinstall_fix_shebang,
    postinstall_generic,
    postinstall_notes,
]
PREUNINSTALL = []

