from typing import Any, Optional
import re
import datetime
from slack.web.client import WebClient as SlackClient

from gg_release_notes.config.env_config import EnvConfig
from gg_release_notes.config.github_config import GithubAPIConfig, GithubAPIConfig
from gg_release_notes.pull_request import ProdReleasePR
from gg_release_notes.version import ReleaseVersion


class UploadRelease:
    def __init__(
        self,
        github_config: GithubAPIConfig,
        env_config: EnvConfig,
        prod_release_pr: ProdReleasePR,
        prod_release_version: ReleaseVersion,
        release_notes_text: str,
    ):
        self.github_config = github_config
        self.github_api = github_config.github_api
        self.env_config = env_config
        self.prod_release_pr = prod_release_pr
        self.prod_release_version = prod_release_version
        self.release_notes_text = release_notes_text

    @property
    def release_prefix(self):
        return "Generated by GPT"

    @property
    def release_title(self):
        return f"Production Release - {self.github_config.owner}/{self.github_config.repository} - {datetime.date.today().isoformat()}"

    @property
    def release_payload(self) -> dict:
        body = f"{self.release_prefix}\n" + self.release_notes_text
        payload = {
            "body": body,
            "title": self.release_title,
        }
        return payload

    def update_label_issues(self) -> Any:
        """Updates issue in Github using Github API.

        Args:
            issue_number (int): https://github.com/{owner}/repository/issues/2906 <- issue number
            payload (dict): containing the keys and values that need to be updated on issue

        Returns:
            dict: request response containing pass/fail message with new issue data
        """
        update_req = self.github_api.issues.update(
            issue_number=self.prod_release_pr.issue_num,
            body=self.release_payload.get("body"),
            title=self.release_title,
        )
        return update_req

    def make_github_release(self, versioning_type: Optional[str]):
        """Creates a release in Github using Github API."""
        if not versioning_type:
            versioning_type = str(self.prod_release_pr.requested_new_version_type())

        new_version = self.prod_release_version.increment_version(
            self.prod_release_version.current_version, versioning_type=versioning_type
        )
        self.github_api.repos.create_release(
            tag_name=new_version,
            name=self.release_title,
            body=self.release_payload.get("body", ""),
            draft=False,
        )

    def upload_to_slack(self, release_notes_text: str):
        """Uploads release notes to Slack"""
        slack_client = SlackClient(self.env_config.SLACK_TOKEN)
        slack_client.chat_postMessage(
            channel=self.env_config.SLACK_CHANNEL_ID,
            text=release_notes_text,
        )
        print("Uploaded release notes to Slack!")

    def upload_to_github(self):
        """Uploads release notes to Github"""
        print(f"Creating Github Release for issue {self.prod_release_pr.issue_num}")
        # Upload release notes to Github if they are not already there
        if not re.findall(
            pattern=self.release_prefix, string=self.prod_release_pr.issue_body()
        ):
            self.update_label_issues()
        else:
            print("Release notes already uploaded to Github")


def upload_release_notes(
    pr_num: str,
    upload_instance: UploadRelease,
    version_type: str = "patch",
    upload_to_github: bool = True,
    make_github_release: bool = False,
    upload_to_slack: bool = False,
):
    """Uploads release notes to Github and/or Slack
    Args:
        pr_num (str):
        releases (str): Release notes to be added to release and/or uploaded to Slack
        upload_to_github (bool): Upload release notes to Github
        make_github_release (bool): Create a Github release
        upload_to_slack (bool): Upload release notes to Slack

    Returns:
        requests.Response: request response containing pass/fail message with new release data
    """

    print(f"Uploading release notes for PR {pr_num}")
    if upload_to_github:
        print("Uploading release notes to Github")
        upload_instance.upload_to_github()

    if make_github_release:
        print("Creating Github release")
        upload_instance.make_github_release(version_type)
    if upload_to_slack:
        print("Uploading release notes to Slack")
        upload_instance.upload_to_slack(upload_instance.release_notes_text)

    print("Done!")
