# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You 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.
"""
Downloads and configures Dynatrace OneAgent.
"""

from __future__ import print_function
import os
import logging
from subprocess import call
import urllib2
import json
import time

_log = logging.getLogger('dynatrace')

class DynatraceInstaller(object):
    def __init__(self, ctx):
        self._log = _log
        self._ctx = ctx
        self._detected = False
        self._run_installer = True
        self.dynatrace_server = None
        try:
            self._log.info("Initializing")
            if ctx['PHP_VM'] == 'php':
                self._log.info("Loading service info")
                self._load_service_info()
        except Exception:
            self._log.exception("Error installing Dynatrace OneAgent! "
                                "Dynatrace OneAgent will not be available.")

    # set 'DYNATRACE_API_URL' if not available
    def _convert_api_url(self):
        if self._ctx['DYNATRACE_API_URL'] == None:
            self._ctx['DYNATRACE_API_URL'] = 'https://' + self._ctx['DYNATRACE_ENVIRONMENT_ID'] + '.live.dynatrace.com/api'

    # verify if 'dynatrace' service is available
    def _load_service_info(self):
        detected_services = []
        vcap_services = self._ctx.get('VCAP_SERVICES', {})
        for provider, services in vcap_services.iteritems():
            for service in services:
                if 'dynatrace' in service.get('name', ''):
                    creds = service.get('credentials', {})
                    if creds.get('environmentid', None) and creds.get('apitoken', None):
                        detected_services.append(creds)
                    else:
                        self._log.info("Dynatrace service detected. But without proper credentials!")

        if len(detected_services) == 1:
            self._log.info("Found one matching Dynatrace service")

            self._ctx['DYNATRACE_API_URL'] = detected_services[0].get('apiurl', None)
            self._ctx['DYNATRACE_ENVIRONMENT_ID'] = detected_services[0].get('environmentid', None)
            self._ctx['DYNATRACE_TOKEN'] = detected_services[0].get('apitoken', None)
            self._ctx['DYNATRACE_SKIPERRORS'] = detected_services[0].get('skiperrors', None)
            self._ctx['DYNATRACE_NETWORK_ZONE'] = detected_services[0].get('networkzone', None)

            self._convert_api_url()
            self._detected = True

        elif len(detected_services) > 1:
            self._log.warning("More than one matching service found!")
            raise SystemExit(1)

    # returns oneagent installer path
    def _get_oneagent_installer_path(self):
        return os.path.join(self._ctx['BUILD_DIR'], 'dynatrace', 'paasInstaller.sh')

    def should_install(self):
        return self._detected

    # create folder if not existing
    def create_folder(self, directory):
        if not os.path.exists(directory):
            os.makedirs(directory)

    def get_buildpack_version(self):
        with open(os.path.join(self._ctx['BP_DIR'], "VERSION")) as version_file:
            return version_file.read()

    def _retry_download(self, url, dest):
        tries = 3
        base_waittime = 3

        for attempt in range(tries):
            try:
                request = urllib2.Request(url)
                request.add_header("user-agent", "cf-php-buildpack/" + self.get_buildpack_version())
                request.add_header("Authorization", "Api-Token {token}".format(token=self._ctx['DYNATRACE_TOKEN']))
                result = urllib2.urlopen(request)
                f = open(dest, 'w')
                f.write(result.read())
                f.close()
                return
            except IOError, exc:
                last_exception = exc
                waittime = base_waittime + 2**attempt
                _log.warn("Error during installer download, retrying in %s seconds" % (waittime))
                time.sleep(waittime)

        raise last_exception

    # downloading the oneagent from the 'DYNATRACE_API_URL'
    def download_oneagent_installer(self):
        self.create_folder(os.path.join(self._ctx['BUILD_DIR'], 'dynatrace'))
        installer = self._get_oneagent_installer_path()
        url = self._ctx['DYNATRACE_API_URL'] + '/v1/deployment/installer/agent/unix/paas-sh/latest?bitness=64&include=php&include=nginx&include=apache'
        skiperrors = self._ctx['DYNATRACE_SKIPERRORS']

        try:
            self._retry_download(url, installer)
            os.chmod(installer, 0o777)
        except IOError, exc:
            if skiperrors == 'true':
                _log.warn('Error during installer download, skipping installation: %s' % (exc))
                self._run_installer = False
            else:
                _log.error('ERROR: Dynatrace agent download failed')
                raise

    def run_installer(self):
        return self._run_installer

    # executing the downloaded oneagent installer
    def extract_oneagent(self):
        installer = self._get_oneagent_installer_path()
        call([installer, self._ctx['BUILD_DIR']])

    # removing the oneagent installer
    def cleanup_oneagent_installer(self):
        installer = self._get_oneagent_installer_path()
        os.remove(installer)

    # copying the exisiting dynatrace-env.sh file
    def adding_environment_variables(self):
        source      = os.path.join(self._ctx['BUILD_DIR'], 'dynatrace', 'oneagent', 'dynatrace-env.sh')
        dest        = os.path.join(self._ctx['BUILD_DIR'], '.profile.d', 'dynatrace-env.sh')
        dest_folder = os.path.join(self._ctx['BUILD_DIR'], '.profile.d')
        self.create_folder(dest_folder)
        os.rename(source, dest)

    # adding LD_PRELOAD to the exisiting dynatrace-env.sh file
    def adding_ld_preload_settings(self):
        envfile    = os.path.join(self._ctx['BUILD_DIR'], '.profile.d', 'dynatrace-env.sh')
        agent_path = None
        manifest_file = os.path.join(self._ctx['BUILD_DIR'], 'dynatrace', 'oneagent', 'manifest.json')

        if os.path.isfile(manifest_file):
            manifest = json.load(open(manifest_file))
            process_technology = manifest['technologies'].get('process')
            if process_technology:
                for entry in process_technology['linux-x86-64']:
                    if entry.get('binarytype') == 'primary':
                        _log.info("Using manifest.json")
                        agent_path = entry['path']

        if not agent_path:
            _log.warning("Agent path not found in manifest.json, using fallback")
            agent_path = os.path.join('agent', 'lib64', 'liboneagentproc.so')

        # prepending agent path with installer directory
        agent_path = os.path.join(self._ctx['HOME'], 'app', 'dynatrace', 'oneagent', agent_path)

        extra_env = '\nexport LD_PRELOAD="{}"'.format(agent_path)
        extra_env += '\nexport DT_LOGSTREAM=${DT_LOGSTREAM:-stdout}'

        network_zone = self._ctx.get('DYNATRACE_NETWORK_ZONE')
        if network_zone:
            extra_env += '\nexport DT_NETWORK_ZONE="${{DT_NETWORK_ZONE:-{}}}"'.format(network_zone)

        with open(envfile, "a") as file:
            file.write(extra_env)


# Extension Methods
def compile(install):
    dynatrace = DynatraceInstaller(install.builder._ctx)
    if dynatrace.should_install():
        _log.info("Downloading Dynatrace OneAgent Installer")
        dynatrace.download_oneagent_installer()
        if dynatrace.run_installer():
            _log.info("Extracting Dynatrace OneAgent")
            dynatrace.extract_oneagent()
            _log.info("Removing Dynatrace OneAgent Installer")
            dynatrace.cleanup_oneagent_installer()
            _log.info("Adding Dynatrace specific Environment Vars")
            dynatrace.adding_environment_variables()
            _log.info("Adding Dynatrace LD_PRELOAD settings")
            dynatrace.adding_ld_preload_settings()
    return 0
