#!/usr/bin/env python2

# THIS FILE IS PART OF THE CYLC SUITE ENGINE.
# Copyright (C) 2008-2018 NIWA & British Crown (Met Office) & Contributors.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

"""cylc [control] ext-trigger [OPTIONS] ARGS

Report an external event message to a suite server program. It is expected that
a task in the suite has registered the same message as an external trigger - a
special prerequisite to be satisifed by an external system, via this command,
rather than by triggering off other tasks.

The ID argument should uniquely distinguish one external trigger event from the
next. When a task's external trigger is satisfied by an incoming message, the
message ID is broadcast to all downstream tasks in the cycle point as
$CYLC_EXT_TRIGGER_ID so that they can use it - e.g. to identify a new data file
that the external triggering system is responding to.

Use the retry options in case the target suite is down or out of contact.

The suite passphrase must be installed in $HOME/.cylc/<SUITE>/.

Note: to manually trigger a task use 'cylc trigger', not this command."""

import sys
from time import sleep

from cylc import LOG
import cylc.flags
from cylc.option_parsers import CylcOptionParser as COP
from cylc.network.httpclient import ClientError, SuiteRuntimeServiceClient


MAX_N_TRIES = 5
RETRY_INTVL_SECS = 10.0

MSG_SEND_FAILED = "Send message: try %s of %s failed"
MSG_SEND_RETRY = "Retrying in %s seconds, timeout is %s"
MSG_SEND_SUCCEED = "Send message: try %s of %s succeeded"


def main():
    parser = COP(
        __doc__, comms=True,
        argdoc=[("REG", "Suite name"),
                ("MSG", "External trigger message"),
                ("ID", "Unique trigger ID")])

    parser.add_option(
        "--max-tries", help="Maximum number of send attempts "
        "(default %s)." % MAX_N_TRIES, metavar="INT",
        action="store", default=MAX_N_TRIES, dest="max_n_tries")

    parser.add_option(
        "--retry-interval", help="Delay in seconds before retrying "
        "(default %s)." % RETRY_INTVL_SECS, metavar="SEC",
        action="store", default=RETRY_INTVL_SECS, dest="retry_intvl_secs")

    (options, args) = parser.parse_args()
    suite, event_msg, event_id = args

    LOG.info('Send to suite %s: "%s" (%s)', suite, event_msg, event_id)

    pclient = SuiteRuntimeServiceClient(
        suite, options.owner, options.host, options.port,
        options.comms_timeout, my_uuid=options.set_uuid,
        print_uuid=options.print_uuid)

    max_n_tries = int(options.max_n_tries)
    retry_intvl_secs = float(options.retry_intvl_secs)

    for i_try in range(max_n_tries):
        try:
            pclient.put_ext_trigger(event_msg, event_id)
        except ClientError as exc:
            LOG.exception(exc)
            LOG.info(MSG_SEND_FAILED, i_try + 1, max_n_tries)
            if i_try == max_n_tries - 1:  # final attempt
                sys.exit('ERROR: send failed')
            else:
                LOG.info(MSG_SEND_RETRY, retry_intvl_secs, pclient.timeout)
                sleep(retry_intvl_secs)
        else:
            if i_try > 0:
                LOG.info(MSG_SEND_SUCCEED, i_try + 1, max_n_tries)
            break


if __name__ == "__main__":
    try:
        main()
    except Exception as exc:
        if cylc.flags.debug:
            raise
        sys.exit(str(exc))
