# -*- encoding: utf-8 -*-
from github import Auth, Github
from github.GitRelease import GitRelease  # pylint: disable=unused-import
from github.Repository import Repository  # pylint: disable=unused-import

from suite_py.lib import logger


class GithubHandler:
    # pylint: disable=too-many-public-methods
    _organization = "primait"

    def __init__(self, tokens):
        auth = Auth.Token(tokens.github)
        self._client = Github(auth=auth)

    def get_repo(self, repo_name):
        return self._client.get_repo(f"{self._organization}/{repo_name}")

    def get_organization(self):
        return self._client.get_organization(self._organization)

    def get_user(self):
        return self._client.get_user()

    # pylint: disable=too-many-positional-arguments
    def create_pr(self, repo, branch, title, body="", base="master", is_draft=False):
        return self.get_repo(repo).create_pull(
            title=title, head=branch, base=base, body=body, draft=is_draft
        )

    def get_pr(self, repo, pr_number):
        return self.get_repo(repo).get_pull(pr_number)

    def get_branch_from_pr(self, repo, pr_number):
        repo = self.get_repo(repo)
        return repo.get_branch(repo.get_pull(pr_number).head.ref)

    def get_tags(self, repo):
        return self.get_repo(repo).get_tags()

    def get_team_members(self, team_name=""):
        return self.get_organization().get_team_by_slug(team_name).get_members()

    def get_all_users(self):
        return self.get_organization().get_members()

    def get_list_pr(self, repo):
        pulls = self.get_repo(repo).get_pulls(
            state="open", sort="created", base="master"
        )
        return pulls

    def get_pr_from_branch(self, repo, branch):
        return self.get_repo(repo).get_pulls(head=f"primait:{branch}")

    def get_link_from_pr(self, repo, pr_number):
        return f"https://github.com/primait/{repo}/pull/{pr_number}"

    def get_commits_since_release(self, repo, tag):
        release_commit = repo.get_commit(tag)
        commits = []
        for c in repo.get_commits():
            if c == release_commit:
                break
            commits.append(c)
        return commits

    def get_latest_release_if_exists(self, repo):
        try:
            return repo.get_latest_release()
        except Exception:
            return None

    def user_is_admin(self, repo):
        return self.get_repo(repo).permissions.admin

    def get_release_if_exists(self, repo, release):
        try:
            return repo.get_release(release)
        except Exception:
            return None

    def get_build_status(self, repo, ref):
        return self.get_repo(repo).get_commit(ref).get_combined_status()

    def get_releases(self, repo):
        return self.get_repo(repo).get_releases()

    def get_branches(self, repo):
        return self.get_repo(repo).get_branches()

    def get_raw_content(self, repo, ref, file):
        repo = self.get_repo(repo)
        content = repo.get_contents(file, ref)
        return content.decoded_content

    def _execute_graphql(self, query, variables):
        """
        Execute a GraphQL query against GitHub's GraphQL API.

        Args:
            query: The GraphQL query string
            variables: Dictionary of variables for the query (can be None)

        Returns:
            dict: The parsed JSON response, or None if an error occurs
        """
        try:
            requester = self._client.requester
            response = requester.graphql_query(query, variables)
            return response
        except Exception as error:
            logger.error(
                "GraphQL query failed: %s. Variables: %s",
                str(error),
                variables,
            )
            return None

    def get_workflow_events_for_commits(self, repo, shas):
        """Fetch workflow events for a list of commit SHAs using GraphQL.

        Args:
            repo: Repository object from PyGithub
            shas: List of commit SHA strings

        Returns:
            dict: Mapping of {check_run_database_id: event_type}
                  Returns empty dict if GraphQL query fails or response contains errors.
                  This allows callers to continue with unfiltered check runs as fallback.

        Raises:
            ValueError: If response parsing fails (raised by _parse_workflow_events_response)
        """
        if not shas:
            return {}

        # Build GraphQL query using variables for commit SHAs (not string interpolation)
        # The GitHub GraphQL API only accepts single oid per object() call
        commit_fragments = []
        variables = {
            "owner": repo.owner.login,
            "repo": repo.name,
        }

        # Add each SHA as a variable and build corresponding query fragment
        for idx, sha in enumerate(shas):
            sha_var = f"sha{idx}"
            variables[sha_var] = sha

            commit_fragments.append(
                f"""
            commit{idx}: object(oid: ${sha_var}) {{
              ... on Commit {{
                oid
                checkSuites(first: 100) {{
                  nodes {{
                    workflowRun {{
                      event
                    }}
                    checkRuns(first: 100) {{
                      nodes {{
                        databaseId
                        name
                      }}
                    }}
                  }}
                }}
              }}
            }}
            """
            )

        # Build query signature with all SHA variables
        sha_params = ", ".join([f"$sha{i}: GitObjectID!" for i in range(len(shas))])
        commit_fragments_str = "\n".join(commit_fragments)
        query = f"""
        query($owner: String!, $repo: String!, {sha_params}) {{
          repository(owner: $owner, name: $repo) {{
            {commit_fragments_str}
          }}
        }}
        """

        response = self._execute_graphql(query, variables)

        if not response or "errors" in response or "data" not in response:
            if response and "errors" in response:
                logger.error(
                    "GraphQL errors for workflow events (repo=%s/%s, commits=%d): %s",
                    repo.owner.login,
                    repo.name,
                    len(shas),
                    response["errors"],
                )
            elif not response:
                logger.warning(
                    "No GraphQL response for workflow events (repo=%s/%s, commits=%d)",
                    repo.owner.login,
                    repo.name,
                    len(shas),
                )
            return {}

        return self._parse_workflow_events_response(response, shas)

    def _parse_workflow_events_response(self, response, shas):
        """Parse GraphQL response to extract workflow events for commits.

        Args:
            response: GraphQL response dict from GitHub API
            shas: List of commit SHA strings (used for indexing aliased queries)

        Returns:
            dict: Mapping of {check_run_database_id: event_type}

        Raises:
            ValueError: If response structure is invalid or unexpected
        """
        result = {}
        try:
            repository = response.get("data", {}).get("repository", {})

            # Iterate through the aliased commit responses
            for idx in range(len(shas)):
                commit_alias = f"commit{idx}"
                commit_data = repository.get(commit_alias)

                if not commit_data:
                    continue

                check_suites = commit_data.get("checkSuites", {}).get("nodes", [])
                for suite in check_suites:
                    if not suite:
                        continue

                    # Handle case where workflowRun is None (manual checks, status checks)
                    workflow_run = suite.get("workflowRun")
                    event_type = (
                        workflow_run.get("event") if workflow_run else "unknown"
                    )

                    check_runs = suite.get("checkRuns", {}).get("nodes", [])
                    for check_run in check_runs:
                        if not check_run:
                            continue

                        database_id = check_run.get("databaseId")
                        if database_id:
                            result[database_id] = event_type

        except (AttributeError, KeyError, TypeError) as error:
            # Log and raise if there's any issue parsing the response
            logger.error(
                "Failed to parse workflow events response: %s. Response structure may be unexpected.",
                str(error),
            )
            raise ValueError(
                f"Failed to parse GraphQL workflow events response: {error}"
            ) from error

        return result
