#!/usr/bin/env python
import json
import argparse
import shutil
import subprocess
import os
import tempfile
import base64

try:
    from urllib.request import urlopen
    from urllib.request import urlretrieve
    from email.utils import parseaddr as parse_author
except ImportError as e:
    # Python 2
    from urllib2 import urlopen
    from urllib import urlretrieve
    from email.Utils import parseaddr as parse_author


PKG_PLACEHOLDER = '__tool__'
URI_MAP = {
    'linux': 'https://tools.sec.yandex-team.ru/api/v1/release/{tool}/linux/{version}/nix_{tool}',
    'windows': 'https://tools.sec.yandex-team.ru/api/v1/release/{tool}/windows/{version}/win_{tool}.exe',
    'darwin': 'https://tools.sec.yandex-team.ru/api/v1/release/{tool}/darwin/{version}/osx_{tool}'
}
NPM_LOGIN = os.environ.get('NPM_LOGIN', '')
NPM_PASSWORD = os.environ.get('NPM_PASSWORD', '')


def safe_copystat(src, dst):
    try:
        shutil.copystat(src, dst)
    except WindowsError:
        # can't copy file access times on Windows
        pass


def copy_template_file(src, dst, pkg_name):
    with open(src, 'rt') as src_f:
        with open(dst, 'wt') as dst_f:
            data = src_f.read().replace(PKG_PLACEHOLDER, pkg_name)
            dst_f.write(data)
    safe_copystat(src, dst)


def copy_template_tree(src, dst, pkg_name):
    if not os.path.exists(dst):
        os.makedirs(dst)

    for name in os.listdir(src):
        src_name = os.path.join(src, name)
        dst_name = os.path.join(dst, name)
        if os.path.islink(src_name):
            shutil.copy(src_name, dst_name)
        elif os.path.isdir(src_name):
            copy_template_tree(src_name, dst_name, pkg_name)
        else:
            copy_template_file(src_name, dst_name, pkg_name)
    safe_copystat(src, dst)


def publish(cwd):
    env = os.environ.copy()
    env['NPM_CONFIG_USERCONFIG'] = os.path.join(cwd, '.default_npmrc')
    if NPM_LOGIN and NPM_PASSWORD:
        env['NPM_LOGIN'] = NPM_LOGIN
        env['NPM_PASSWORD'] = base64.standard_b64encode(NPM_PASSWORD.encode('ascii'))
    proc = subprocess.Popen(['npm', 'publish'], env=env, cwd=cwd)
    proc.communicate()
    if proc.returncode:
        raise Exception('Failed to publish tool, exit code: {code}'.format(
            code=proc.returncode)
        )


def build(work_dir, tool_name, version, platform):
    uri = URI_MAP[platform].format(
        tool=tool_name,
        version=version
    )
    target = os.path.join(work_dir, 'vendor', os.path.basename(uri))

    try:
        urlretrieve(uri, target)
    except Exception as e:
        raise Exception('Failed to download tool from url %s: %s' % (uri, e))
    os.chmod(target, 0o755)


def sync(cwd, tool_name, platform, version, author, author_email, pack_binaries):
    uri = 'https://tools.sec.yandex-team.ru/api/v1/release/{name}/{platform}/{version}/info'.format(
        name=tool_name,
        platform=platform,
        version=version
    )
    body = urlopen(uri)
    info = json.loads(body.read())
    version = info['result']['version']

    pkgjson_path = os.path.join(cwd, 'package.json')
    with open(pkgjson_path, 'rt') as f:
        package_json = json.loads(f.read().strip())
        package_json['version'] = version
        package_json['author'] = {
            'name': author,
            'email': author_email
        }
        if not pack_binaries:
            package_json['scripts'] = {
                'postinstall': 'node lib/install.js'
            }

    with open(pkgjson_path, 'wt') as f:
        f.write(json.dumps(package_json, sort_keys=True, indent=4))

    print('%s version synced to version: %s' % (tool_name, version))


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument('tool', type=str,
                        help='security tool name')
    parser.add_argument('--version', type=str, default='latest',
                        help='secuity tool version')
    parser.add_argument('--platforms', type=str, default='linux, windows, darwin',
                        help='comma-separated list of needed platforms')
    parser.add_argument('--workdir', type=str, default='',
                        help='work directory')
    parser.add_argument('--author', type=str,
                        help='package author in email format')
    parser.add_argument('--pack-binaries', default=False, action='store_true',
                        help='pack binaries into the package')

    args = parser.parse_args()
    tool_name = args.tool
    version = args.version
    pack_binaries = args.pack_binaries
    platforms = [x.strip() for x in args.platforms.split(',')]
    if args.author:
        author, author_email = parse_author(args.author)
    else:
        author, author_email = 'unknown', 'sectools@yandex-team.ru'
    if not args.workdir:
        work_dir = tempfile.mkdtemp()
    else:
        work_dir = args.workdir
    tmpl_folder = 'template-embedded' if pack_binaries else 'template'
    src_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), tmpl_folder)

    print('copy %s to %s' % (src_dir, work_dir))
    copy_template_tree(src_dir, work_dir, tool_name)

    print('try to sync version')
    sync(work_dir, tool_name, platforms[0], version, author, author_email, pack_binaries)

    if pack_binaries:
        for platform in platforms:
            print('download binary for platform: ' + platform)
            build(work_dir, tool_name, version, platform)

    print('publish packages')
    publish(work_dir)
