#!/usr/bin/python
# -*- coding: utf-8 -*-

# Copyright (c) 2019, Prasad Katti (@prasadkatti)
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

DOCUMENTATION = r"""
---
module: stepfunctions_state_machine_execution
version_added: 1.0.0

short_description: Start or stop execution of an AWS Step Functions state machine

description:
    - Start or stop execution of a state machine in AWS Step Functions.
    - Prior to release 5.0.0 this module was called C(community.aws.aws_step_functions_state_machine_execution).
      The usage did not change.

options:
    action:
        description: Desired action (C(start) or C(stop)) for a state machine execution.
        default: start
        choices: [ start, stop ]
        type: str
    name:
        description: Name of the execution.
        type: str
    execution_input:
        description: The JSON input data for the execution.
        type: json
        default: {}
    state_machine_arn:
        description: The ARN of the state machine that will be executed.
        type: str
    execution_arn:
        description: The ARN of the execution you wish to stop.
        type: str
    cause:
        description: A detailed explanation of the cause for stopping the execution.
        type: str
        default: ''
    error:
        description: The error code of the failure to pass in when stopping the execution.
        type: str
        default: ''

author:
    - Prasad Katti (@prasadkatti)

extends_documentation_fragment:
    - amazon.aws.common.modules
    - amazon.aws.region.modules
    - amazon.aws.boto3
"""

EXAMPLES = r"""
- name: Start an execution of a state machine
  community.aws.stepfunctions_state_machine_execution:
    name: an_execution_name
    execution_input: '{ "IsHelloWorldExample": true }'
    state_machine_arn: "arn:aws:states:us-west-2:123456789012:stateMachine:HelloWorldStateMachine"

- name: Stop an execution of a state machine
  community.aws.stepfunctions_state_machine_execution:
    action: stop
    execution_arn: "arn:aws:states:us-west-2:123456789012:execution:HelloWorldStateMachineCopy:a1e8e2b5-5dfe-d40e-d9e3-6201061047c8"
    cause: "cause of task failure"
    error: "error code of the failure"
"""

RETURN = r"""
execution_arn:
    description: ARN of the AWS Step Functions state machine execution.
    type: str
    returned: if action == start and changed == True
    sample: "arn:aws:states:us-west-2:123456789012:execution:HelloWorldStateMachineCopy:a1e8e2b5-5dfe-d40e-d9e3-6201061047c8"
start_date:
    description: The date the execution is started.
    type: str
    returned: if action == start and changed == True
    sample: "2019-11-02T22:39:49.071000-07:00"
stop_date:
    description: The date the execution is stopped.
    type: str
    returned: if action == stop
    sample: "2019-11-02T22:39:49.071000-07:00"
"""


try:
    import botocore
except ImportError:
    pass  # caught by AnsibleAWSModule

from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict

from ansible_collections.amazon.aws.plugins.module_utils.botocore import is_boto3_error_code

from ansible_collections.community.aws.plugins.module_utils.modules import AnsibleCommunityAWSModule as AnsibleAWSModule


def start_execution(module, sfn_client):
    """
    start_execution uses execution name to determine if a previous execution already exists.
    If an execution by the provided name exists, call client.start_execution will not be called.
    """

    state_machine_arn = module.params.get("state_machine_arn")
    name = module.params.get("name")
    execution_input = module.params.get("execution_input")

    try:
        # list_executions is eventually consistent
        page_iterators = sfn_client.get_paginator("list_executions").paginate(stateMachineArn=state_machine_arn)

        for execution in page_iterators.build_full_result()["executions"]:
            if name == execution["name"]:
                check_mode(module, msg="State machine execution already exists.", changed=False)
                module.exit_json(changed=False)

        check_mode(module, msg="State machine execution would be started.", changed=True)
        res_execution = sfn_client.start_execution(stateMachineArn=state_machine_arn, name=name, input=execution_input)
    except is_boto3_error_code("ExecutionAlreadyExists"):
        # this will never be executed anymore
        module.exit_json(changed=False)
    except (
        botocore.exceptions.ClientError,
        botocore.exceptions.BotoCoreError,
    ) as e:  # pylint: disable=duplicate-except
        module.fail_json_aws(e, msg="Failed to start execution.")

    module.exit_json(changed=True, **camel_dict_to_snake_dict(res_execution))


def stop_execution(module, sfn_client):
    cause = module.params.get("cause")
    error = module.params.get("error")
    execution_arn = module.params.get("execution_arn")

    try:
        # describe_execution is eventually consistent
        execution_status = sfn_client.describe_execution(executionArn=execution_arn)["status"]
        if execution_status != "RUNNING":
            check_mode(module, msg="State machine execution is not running.", changed=False)
            module.exit_json(changed=False)

        check_mode(module, msg="State machine execution would be stopped.", changed=True)
        res = sfn_client.stop_execution(executionArn=execution_arn, cause=cause, error=error)
    except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
        module.fail_json_aws(e, msg="Failed to stop execution.")

    module.exit_json(changed=True, **camel_dict_to_snake_dict(res))


def check_mode(module, msg="", changed=False):
    if module.check_mode:
        module.exit_json(changed=changed, output=msg)


def main():
    module_args = dict(
        action=dict(choices=["start", "stop"], default="start"),
        name=dict(type="str"),
        execution_input=dict(type="json", default={}),
        state_machine_arn=dict(type="str"),
        cause=dict(type="str", default=""),
        error=dict(type="str", default=""),
        execution_arn=dict(type="str"),
    )
    module = AnsibleAWSModule(
        argument_spec=module_args,
        required_if=[
            ("action", "start", ["name", "state_machine_arn"]),
            ("action", "stop", ["execution_arn"]),
        ],
        supports_check_mode=True,
    )

    sfn_client = module.client("stepfunctions")

    action = module.params.get("action")
    if action == "start":
        start_execution(module, sfn_client)
    else:
        stop_execution(module, sfn_client)


if __name__ == "__main__":
    main()
