# Copyright 2012 Hewlett-Packard Development Company, L.P.
#
# 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.


"""
Wrappers can alter the way the build is run as well as the build output.

**Component**: wrappers
  :Macro: wrapper
  :Entry Point: jenkins_jobs.wrappers

"""

import logging
import xml.etree.ElementTree as XML
import pkg_resources
import jenkins_jobs.modules.base
from jenkins_jobs.errors import (JenkinsJobsException,
                                 InvalidAttributeError,
                                 MissingAttributeError)
from jenkins_jobs.modules.builders import create_builders
from jenkins_jobs.modules.helpers import config_file_provider_builder

logger = logging.getLogger(__name__)

MIN_TO_SEC = 60


def ci_skip(parser, xml_parent, data):
    """yaml: ci-skip
    Skip making a build for certain push.
    Just add [ci skip] into your commit's message to let Jenkins know,
    that you do not want to perform build for the next push.
    Requires the Jenkins :jenkins-wiki:`Ci Skip Plugin <Ci+Skip+Plugin>`.

    Example:

    .. literalinclude:: /../../tests/wrappers/fixtures/ci-skip001.yaml
    """
    rpobj = XML.SubElement(xml_parent, 'ruby-proxy-object')
    robj = XML.SubElement(rpobj, 'ruby-object', attrib={
        'pluginid': 'ci-skip',
        'ruby-class': 'Jenkins::Tasks::BuildWrapperProxy'
    })
    pluginid = XML.SubElement(robj, 'pluginid', {
        'pluginid': 'ci-skip', 'ruby-class': 'String'
    })
    pluginid.text = 'ci-skip'
    obj = XML.SubElement(robj, 'object', {
        'ruby-class': 'CiSkipWrapper', 'pluginid': 'ci-skip'
    })
    XML.SubElement(obj, 'ci__skip', {
        'pluginid': 'ci-skip', 'ruby-class': 'NilClass'
    })


def config_file_provider(parser, xml_parent, data):
    """yaml: config-file-provider
    Provide configuration files (i.e., settings.xml for maven etc.)
    which will be copied to the job's workspace.
    Requires the Jenkins :jenkins-wiki:`Config File Provider Plugin
    <Config+File+Provider+Plugin>`.

    :arg list files: List of managed config files made up of three
      parameters

      :files: * **file-id** (`str`) -- The identifier for the managed config
                file
              * **target** (`str`) -- Define where the file should be created
                (optional)
              * **variable** (`str`) -- Define an environment variable to be
                used (optional)

    Example:

    .. literalinclude:: \
    /../../tests/wrappers/fixtures/config-file-provider003.yaml
       :language: yaml
    """
    cfp = XML.SubElement(xml_parent, 'org.jenkinsci.plugins.configfiles.'
                         'buildwrapper.ConfigFileBuildWrapper')
    cfp.set('plugin', 'config-file-provider')
    config_file_provider_builder(cfp, data)


def logfilesize(parser, xml_parent, data):
    """yaml: logfilesize
    Abort the build if its logfile becomes too big.
    Requires the Jenkins :jenkins-wiki:`Logfilesizechecker Plugin
    <Logfilesizechecker+Plugin>`.

    :arg bool set-own: Use job specific maximum log size instead of global
        config value (default false).
    :arg bool fail: Make builds aborted by this wrapper be marked as "failed"
        (default false).
    :arg int size: Abort the build if logfile size is bigger than this
        value (in MiB, default 128). Only applies if set-own is true.

    Minimum config example:

    .. literalinclude:: /../../tests/wrappers/fixtures/logfilesize002.yaml

    Full config example:

    .. literalinclude:: /../../tests/wrappers/fixtures/logfilesize001.yaml

    """
    lfswrapper = XML.SubElement(xml_parent,
                                'hudson.plugins.logfilesizechecker.'
                                'LogfilesizecheckerWrapper')
    lfswrapper.set("plugin", "logfilesizechecker")

    XML.SubElement(lfswrapper, 'setOwn').text = str(
        data.get('set-own', 'false')).lower()
    XML.SubElement(lfswrapper, 'maxLogSize').text = str(
        data.get('size', '128')).lower()
    XML.SubElement(lfswrapper, 'failBuild').text = str(
        data.get('fail', 'false')).lower()


def timeout(parser, xml_parent, data):
    """yaml: timeout
    Abort the build if it runs too long.
    Requires the Jenkins :jenkins-wiki:`Build Timeout Plugin
    <Build-timeout+Plugin>`.

    :arg bool fail: Mark the build as failed (default false)
    :arg bool abort: Mark the build as aborted (default false)
    :arg bool write-description: Write a message in the description
        (default false)
    :arg int timeout: Abort the build after this number of minutes (default 3)
    :arg str timeout-var: Export an environment variable to reference the
        timeout value (optional)
    :arg str type: Timeout type to use (default absolute)
    :type values:
        * **likely-stuck**
        * **no-activity**
        * **elastic**
        * **absolute**

    :arg int elastic-percentage: Percentag
e of the three most recent builds
        where to declare a timeout, only applies to **elastic** type.
        (default 0)
    :arg int elastic-number-builds: Number of builds to consider computing
        average duration, only applies to **elastic** type. (default 3)
    :arg int elastic-default-timeout: Timeout to use if there were no previous
        builds, only applies to **elastic** type. (default 3)

    Example (Version < 1.14):

    .. literalinclude:: /../../te
sts/wrappers/fixtures/timeout/timeout001.yaml

    .. literalinclude:: /../../tests/wrappers/fixtures/timeout/timeout002.yaml

    .. literalinclude:: /../../tests/wrappers/fixtures/timeout/timeout003.yaml

    Example (Version >= 1.14):

    .. literalinclude::
        /../../tests/wrappers/fixtures/timeout/version-1.14/absolute001.yaml

    .. literalinclude::
        /../../tests/wrappers/fixtures/timeout/version-1.14/no-activity001.yaml

    .. literalinclude::
        /../../tests/wrappers/fixtures/timeout/version-1.14/likely-stuck001.yaml

    .. literalinclude::
        /../../tests/wrappers/fixtures/timeout/version-1.14/elastic001.yaml

    """
    prefix = 'hudson.plugins.build__timeout.'
    twrapper = XML.SubElement(xml_parent, prefix + 'BuildTimeoutWrapper')

    plugin_info = parser.registry.get_plugin_info(
        "Jenkins build timeout plugin")
    version = pkg_resources.parse_version(plugin_info.get("version", "0"))

    valid_strategies = ['absolute', 'no-activity', 'likely-stuck', 'elastic']

    if version >= pkg_resources.parse_version("1.14"):
        strategy = data.get('type', 'absolute')
        if strategy not in valid_strategies:
            InvalidAttributeError('type', strategy, valid_strategies)

        if strategy == "absolute":
            strategy_element = XML.SubElement(
                twrapper, 'strategy',
                {'class': "hudson.plugins.build_timeout."
                          "impl.AbsoluteTimeOutStrategy"})
            XML.SubElement(strategy_element, 'timeoutMinutes'
                           ).text = str(data.get('timeout', 3))
        elif strategy == "no-activity":
            strategy_element = XML.SubElement(
                twrapper, 'strategy',
                {'class': "hudson.plugins.build_timeout."
                          "impl.NoActivityTimeOutStrategy"})
            timeout_sec = int(data.get('timeout', 3)) * MIN_TO_SEC
            XML.SubElement(strategy_element,
                           'timeoutSecondsString').text = str(timeout_sec)
        elif strategy == "likely-stuck":
            strategy_element = XML.SubElement(
                twrapper, 'strategy',
                {'class': "hudson.plugins.build_timeout."
                          "impl.LikelyStuckTimeOutStrategy"})
            XML.SubEle