import re
from pathlib import Path

import click
import yaml
from git import Repo
from github import Auth, Github
from loguru import logger

from odoo_tools_grap.cli.click_options import option_repo_config_file

from .tools import git_checkout, git_fetch, git_pull_rebase, git_push


@click.command()
@click.option("-v", "--version", type=str, required=True)
@click.option(
    "-r", "--remotes", type=str, required=True, default=["grap"], multiple=True
)
@click.option("-p", "--projects", type=str, default=[], multiple=True)
@click.option(
    "-g",
    "--github-token-file",
    type=click.Path(
        exists=True,
        dir_okay=False,
        resolve_path=True,
    ),
)
@option_repo_config_file
@click.pass_context
def rebase(ctx, version, remotes, projects, github_token_file, config_repo_file):
    """parse a repo.yml config file, and for each branch,
    rebase the branch and push the result (if it's our branch)
    'Our' is defined with the remote argument.
    """
    logger.info(f"Read github token in '{github_token_file}' ...")
    github_token_file = Path(github_token_file)
    with github_token_file.open(mode="r") as file:
        token = file.read()

    gh = Github(auth=Auth.Token(token))

    config_repo_file = Path(config_repo_file)

    # Compute Addons path
    stream = open(config_repo_file)
    data = yaml.safe_load(stream)

    for addons_path, repo_data in data.items():
        if projects and addons_path.replace("./src/", "") not in projects:
            logger.info(f"[SKIP] {addons_path}.")
            continue
        repo_name = addons_path.split("/")[-1]

        if addons_path == "./src/odoo":
            distant_remote = "odoo"
            # <TODO>
            # Remove this hugly code.
            # In recent grap instance, we base our code on odoo
            # not OCA/OCB
            if version == "12.0":
                distant_remote = "OCA"
                repo_name = "OCB"
            # </TODO>
        elif addons_path == "./src/openupgrade":
            distant_remote = "OCA"
        else:
            distant_remote = addons_path.split("/")[-2]

        path = config_repo_file.cwd() / addons_path
        logger.info(f"Scanning repository '{addons_path}' ...")

        if not path.exists():
            logger.error(
                f"[NOT FOUND] The folder '{addons_path}' has not been found."
                " Maybe you could run:\n"
                f" gitaggregate -c repos.yml -d {addons_path}"
            )
            continue

        repo = Repo(str(path))

        branch_datas = []

        for merge_data in repo_data["merges"]:
            merge_data = merge_data.split(" ")
            pr_remote = merge_data[0]

            if "refs/pull/" in merge_data[1]:
                logger.debug(f"deduce from {merge_data}")
                # We deduce remote and branch name from the pull number
                pr_number = int(re.search(r"pull\/(\d*)", merge_data[1]).group(1))
                gh_repo = gh.get_repo(f"{pr_remote}/{repo_name}")
                try:
                    gh_pr = gh_repo.get_pull(pr_number)
                except Exception:
                    logger.error(
                        f"Unable to get the branch of {pr_remote}/{repo_name}/{pr_number}"
                    )
                    continue
                if gh_pr.is_merged():
                    continue
                merge_data = gh_pr.head.label.replace(":", " ").split(" ")
                branch_datas.append(
                    {
                        "remote": merge_data[0],
                        "branch": merge_data[1],
                        "pr_remote": pr_remote,
                    }
                )

            else:
                branch_datas.append({"remote": merge_data[0], "branch": merge_data[1]})

        # Removing the main branch
        branch_datas = branch_datas[1:]

        for branch_data in branch_datas:
            if branch_data["remote"] not in remotes:
                logger.info(f"[SKIP] {branch_data['remote']} {branch_data['branch']}")
                continue
            logger.info(f"Work on {branch_data['remote']} {branch_data['branch']} ...")
            git_fetch(branch_data["remote"], branch_data["branch"], cwd=path)
            git_checkout(branch_data["branch"], remote=branch_data["remote"], cwd=path)
            last_commit = repo.active_branch.commit.hexsha
            if branch_data.get("pr_remote"):
                remote = branch_data["pr_remote"]
            else:
                remote = distant_remote
            git_pull_rebase(remote, version, cwd=path)
            new_last_commit = repo.active_branch.commit.hexsha
            if last_commit != new_last_commit:
                logger.warning("Commit Changed, pushing new rebased branch ...")
                git_push(
                    branch_data["remote"],
                    branch_data["branch"],
                    force_push=True,
                    cwd=path,
                )
