"""Proxy for interacting with Github."""

import re

import boto3
from github import Github, GithubException

import config
import lambdalogging

LOG = lambdalogging.getLogger(__name__)

SAR_APP_URL = ('https://serverlessrepo.aws.amazon.com/applications/arn:aws:serverlessrepo:us-east-1:277187709615:'
               'applications~github-codebuild-logs')
SAR_HOMEPAGE = 'https://aws.amazon.com/serverless/serverlessrepo/'

HIDDEN_COMMENT = """
<!--
CREATED BY GITHUB-CODEBUILD-LOGS
-->
"""

PR_COMMENT_TEMPLATE = f"""
### AWS CodeBuild CI Report

* CodeBuild project: {{project_name}}
* Commit ID: {{commit_id}}
* Result: {{build_status}}
* [Build Logs]({{logs_url}}) (available for {config.EXPIRATION_IN_DAYS} days)

*Powered by [github-codebuild-logs]({SAR_APP_URL}),\
 available on the [AWS Serverless Application Repository]({SAR_HOMEPAGE})*

{HIDDEN_COMMENT}
"""

CODEBUILD = boto3.client('codebuild')
SECRETS_MANAGER = boto3.client('secretsmanager')


class GithubProxy:
    """Encapsulate interactions with Github."""

    def __init__(self):
        """Initialize proxy."""
        pass

    def publish_pr_comment(self, build):
        """Publish PR comment with link to build logs."""
        pr_comment = PR_COMMENT_TEMPLATE.format(
            project_name=config.PROJECT_NAME,
            commit_id=build.commit_id,
            build_status=build.status,
            logs_url=build.get_logs_url(),
        )

        repo = self._get_repo()
        LOG.debug('Publishing PR Comment: repo=%s/%s, pr_id=%s, comment=%s',
                  self._github_owner, self._github_repo, build.get_pr_id(), pr_comment)
        repo.get_pull(build.get_pr_id()).create_issue_comment(pr_comment)

    def delete_previous_comments(self, build):
        """Delete previous PR comments."""
        repo = self._get_repo()
        for comment in repo.get_issue(build.get_pr_id()).get_comments():
            if HIDDEN_COMMENT in comment.body:  # Check for hidden comment in body
                try:  # Not critical, catch all GitHub exceptions here
                    LOG.debug('Deleting previous comment: repo=%s/%s, pr_id=%s, comment_id=%s',
                              self._github_owner, self._github_repo, build.get_pr_id(), comment.id)
                    comment.delete()
                except GithubException as e:
                    LOG.warning('Failed to delete previous comment: repo=%s/%s, pr_id=%s, comment_id=%s, error=%s',
                                self._github_owner, self._github_repo, build.get_pr_id(), comment.id, str(e))

    def _get_repo(self):
        if not hasattr(self, '_repo'):
            gh_client = self._get_client()
            self._repo = gh_client.get_user(self._github_owner).get_repo(self._github_repo)
        return self._repo

    def _get_client(self):
        if not hasattr(self, '_client'):
            self._init_client()
        return self._client

    def _init_client(self):
        self._init_github_info()
        self._client = Github(self._github_token)

    def _init_github_info(self):
        response = CODEBUILD.batch_get_projects(
            names=[config.PROJECT_NAME]
        )

        project_details = response['projects'][0]
        if project_details['source']['type'] != 'GITHUB':
            raise RuntimeError(
                'AWS CodeBuild project {} source is not GITHUB. Project source must be of type GITHUB'.format(
                    config.PROJECT_NAME))

        # if user provided an OAuth token to use, fetch it from secrets manager
        if config.GITHUB_OAUTH_TOKEN_SECRET_ARN:
            secret_response = SECRETS_MANAGER.get_secret_value(SecretId=config.GITHUB_OAUTH_TOKEN_SECRET_ARN)
            self._github_token = secret_response['SecretString']
        # if user did not provide an OAuth token to use, try to get one from the CodeBuild project
        elif project_details['source'].get('auth', {}).get('type') == 'OAUTH':
            self._github_token = project_details['source']['auth']['resource']
        else:
            raise RuntimeError(
                'Could not get GitHub OAuth token from AWS CodeBuild project {}. Please use the GitHubOAuthToken app'
                ' parameter to specify a token to use when writing to GitHub.'.format(config.PROJECT_NAME))

        github_location = project_details['source']['location']
        matches = re.search(r'github\.com\/(.+)\/(.+)\.git$', github_location)
        if not matches:
            raise RuntimeError(
                'Could not parse GitHub owner/repo name from AWS CodeBuild project {}. location={}'.format(
                    config.PROJECT_NAME, github_location))

        self._github_owner = matches.group(1)
        self._github_repo = matches.group(2)