from typing import Optional

import trio
import typer
from tqdm import tqdm

from invisible_hand.config import app_context
from ..ensures import ensure_config_exists, ensure_gh_token
from ..utils.github_entities import Team
from ..utils.github_scanner import query_matching_repos
from .shared_options import opt_all_yes, opt_dry, opt_gh_org, opt_github_token


def grant_read_access(
    # @TODO: add autocompletion for option
    # https://typer.tiangolo.com/tutorial/options/autocompletion/
    hw_title: str = typer.Argument(
        default=..., metavar="📝hw", help="target homework to grant access to"
    ),
    team: Optional[str] = typer.Option(
        None, "--team", help="team-slug of the Teaching teams", show_default=True,
    ),
    dry: bool = opt_dry,
    yes: bool = opt_all_yes,
    token: Optional[str] = opt_github_token,
    org: Optional[str] = opt_gh_org,
):
    """Make TAs being able to read all homework repos

    "team-slug": the field auto generated by github.

    For example: the team-slug of "2019 Teaching-team" would be "2019_teaching-team".
    """
    ensure_config_exists()
    print("start script")

    def fallback(val, fallback_value):
        return val if val else fallback_value

    # Handle default value manually because we'll change our config after app starts up
    token: str = fallback(token, app_context.config.github.personal_access_token)
    org: str = fallback(org, app_context.config.github.organization)
    team: str = fallback(team, app_context.config.grant_read_access.reader_team_slug)

    ensure_gh_token(token)
    if not (
        yes or typer.confirm(f"Grant read access of {org}/{hw_title} to {org}/{team}?")
    ):
        raise typer.Abort()

    teaching_team = Team(org, team, token, dry)

    repos = query_matching_repos(
        org, github_repo_prefix=f"{hw_title}-", github_token=token, verbose=True
    )
    repo_names = [r["name"] for r in repos]

    # show repos to operate on
    ncols = 3
    cols = [repo_names[i::ncols] for i in range(ncols)]
    print("repos to operate on:")
    for a, b, c in zip(*cols):
        print(f"  {a:<30}{b:<30}{c:<}")

    builder = pbar_builder()
    builder.set_config(total=len(repo_names))
    with builder.build(desc="fired") as fired, builder.build(desc="returned") as returned:

        async def subscribe_to_repo(team: Team, repo_name: str):
            res = await team.add_team_repository_async(repo_name, permission="pull")
            if res.status_code != 204:
                print(res)
            returned.update(1)

        async def async_github():
            async with trio.open_nursery() as nursery:
                for repo_name in repo_names:
                    if not dry:
                        nursery.start_soon(subscribe_to_repo, teaching_team, repo_name)
                    fired.update(1)
                    fired.refresh()
                return

        trio.run(async_github)


class pbar_builder:
    def __cover_dict(self, src, dst):
        for k, v in src.items():
            dst[k] = v

    def __init__(self):
        self.config = {}
        self.next_po = 0

    def set_config(self, **kwargs):
        self.__cover_dict(kwargs, self.config)

    def build(self, **kwargs):
        cfg = {}
        self.__cover_dict(self.config, cfg)
        self.__cover_dict(kwargs, cfg)
        cfg["position"] = self.next_po
        self.next_po += 1
        return tqdm(**cfg)
