#!/usr/bin/env python

import importlib.metadata
from typing import List, Tuple, Union

from jira import Issue

from jiav import content, exceptions, logger, manifest
from jiav.jira import JiraConnection

jiav_logger = logger.subscribe_to_logger()


def prepare_jiav_comment(
    status: str,
    manifest_comment: str,
    successful: bool = False,
    upload_attachment: bool = False,
) -> str:
    """
    Prepares jiav comment containing the execution result

    Arguments:
        successful       - Result of jiav manifest execution
        status           - Desired status of verified issue
        manifest_comment - Jira comment containing jiav manifest
        attachment       - Attachment to uploaded if required

    Returns:
        generated_comment - Comment that will be posted
    """
    generated_comment: List = []
    text_body: List = []
    # If jiav manifest execution is successful
    text_body.append(f"Executed manifest on comment ID: *{{{{{manifest_comment}}}}}*.")
    if successful:
        text_body.append("Manifest executed successfully.")
        text_body.append(f"Verifying issue as *{{{{{status}}}}}*.")
    # If jiav manifest execution failed
    else:
        text_body.append("Failed to verify issue using provided manifest.")
    if upload_attachment:
        text_body.append(
            "Uploading attachment of execution, *attachment is not private*."
        )
    else:
        text_body.append("Attachment was not uploaded, view logs on executor.")
    generated_comment.append("{panel:title=jiav Report}")
    generated_comment.append("\n".join(text_body))
    generated_comment.append("{panel}")
    generated_comment.append(
        " ".join(
            [
                "Generated by [jiav|https://github.com/vkhitrin/jiav],",
                f"version: {{{{{importlib.metadata.version('jiav')}}}}}.",
            ]
        )
    )
    return "\n".join(generated_comment)


def process_comments(
    comments: List, allow_public_comments: bool = False
) -> Tuple[Union[manifest.Manifest, bool], str]:
    valid_manifest: Union[manifest.Manifest, bool] = False
    jiav_comment: str = ""
    # Iterate over comments in reverse order (from latest to oldest)
    for idx, comment in reversed(list(enumerate(comments))):
        jiav_logger.debug(f"Looking at comment '#{idx}'")
        # Attempt to validate comment according to jiav manifest
        if not allow_public_comments:
            try:
                comment.visibility.type
            except AttributeError:
                jiav_logger.debug(f"Comment {idx} is not private, skipping")
                continue
        try:
            valid_manifest = manifest.Manifest(manifest_text=comment.body)
        except exceptions.InvalidManifestException:
            jiav_logger.debug(f"Comment {idx} does not contain a valid manifest")
        except exceptions.InvalidBackend as e:
            jiav_logger.error(e)
        if valid_manifest:
            jiav_comment = comment
            break
    return valid_manifest, jiav_comment


def verify_issues(
    jira_connection: JiraConnection,
    issues: List[Issue] = [],
    upload_attachment: bool = False,
    allow_public_comments: bool = False,
    no_comment_on_failure: bool = False,
    dry_run: bool = False,
) -> List[Issue]:
    """
    Attempts to verify issues

    Arguments:
        issues - List of issues from jiav.utils.jira.discover_valid_issues function

        jira_connection - Jira connection object defined by
        jiav.utils.jira.JiraConnection

        upload_attacment - Upload attachment if requested

        public_comment_allowed - Allow manifest from public comments

        dry_run - Do not update issues

    Returns:
        verified_issues - List of issues that were successfully verified
    """
    # Init variables
    verified_issues: List[Issue] = []
    # Iterate over issues
    for issue in issues:
        jiav_manifest: Union[manifest.Manifest, bool] = False
        manifest_comment: str = ""
        desired_status: str = ""
        jiav_logger.info(f"Looking at issue '{issue}'")
        jiav_manifest, manifest_comment = process_comments(
            comments=issue.fields.comment.comments,
            allow_public_comments=allow_public_comments,
        )
        # If valid manifest was provided in comments
        if isinstance(jiav_manifest, manifest.Manifest):
            jiav_logger.info(f"Valid manifest was found in issue '{issue}'")
            desired_status = jiav_manifest.verified_status
            current_status: str = issue.fields.status.name
            # If issue is already in the desired status, we skip it
            if desired_status == current_status:
                jiav_logger.info(
                    " ".join(
                        [
                            f"Issue '{issue}' is already in the",
                            f"desired status '{desired_status}'",
                        ]
                    )
                )
                break
            transition_id = jira_connection.check_if_status_is_valid(
                issue=issue, desired_status=desired_status
            )
            if not transition_id:
                jiav_logger.error(
                    " ".join(
                        [
                            f"Desired status '{desired_status}' is not valid in",
                            "this instance or it can not be",
                            "trasnsitioned from current status",
                            f"'{current_status}'",
                        ]
                    )
                )
                break
            # Execute according to jiav request
            try:
                jiav_manifest.execute_manifest()
            except exceptions.BackendExecutionFailed as e:
                jiav_logger.exception(e)
            if not dry_run:
                comment = prepare_jiav_comment(
                    successful=jiav_manifest.successful,
                    status=jiav_manifest.verified_status,
                    manifest_comment=manifest_comment,
                    upload_attachment=upload_attachment,
                )
                # Post comment with the execution when applicable
                if not no_comment_on_failure or jiav_manifest.successful:
                    jira_connection.post_comment(issue=issue, comment=comment)

                # Add attachment if requested by user
                if upload_attachment:
                    # Generate tempfile with the jiav manifest execution output
                    try:
                        temp_file = content.write_content_to_tempfile(
                            jiav_manifest.execution_output
                        )
                        jira_connection.upload_attachment(
                            issue=issue, file_path=temp_file.name
                        )
                    except AttributeError:
                        jiav_logger.error(
                            " ".join(
                                [
                                    "Failed to create attachment, no execution",
                                    "output in manifest execution",
                                ]
                            )
                        )

                # If manifest executed successfully
                if jiav_manifest.successful:
                    jira_connection.update_issue_status(
                        issue=issue, transition_id=transition_id
                    )
                    verified_issues.append(issue)
        else:
            jiav_logger.error(f"Valid manifest was not found in issue '{issue}'")
    return verified_issues
