import click
import os
import git


import urnc.util as util

import urnc.logger as log


def get_upstream_changes(repo):
    branch_name = repo.active_branch
    changes = repo.git.diff(
        f"..origin/{branch_name}", "--name-status").split('\n')
    return changes


def get_upstream_deleted(repo):
    changes = get_upstream_changes(repo)
    files = []
    for change in changes:
        parts = change.split('\t', 1)
        if (len(parts) != 2):
            continue
        [ctype, file] = change.split('\t', 1)
        if (ctype == "D"):
            files.append(file)
    return files


def get_upstream_added(repo):
    changes = get_upstream_changes(repo)
    files = []
    for change in changes:
        parts = change.split('\t', 1)
        if (len(parts) != 2):
            continue
        [ctype, file] = parts
        if (ctype == "A"):
            files.append(file)
    return files


def get_course_info(course_name, output):
    base_url = os.getenv("GIT_URL")
    if not base_url:
        log.error("Failed to pull. Missing env var GIT_URL")
        return (None, None)

    if not course_name:
        try:
            git_repo = git.Repo(os.getcwd(), search_parent_directories=True)
            git_url = git_repo.remote().url
            if not git_url.startswith(base_url):
                log.error(f"Failed to pull. Invalid git remote_url {git_url}")
                return (None, None)
            folder_name = git_repo.working_dir
            return (git_url, folder_name)
        except Exception:
            log.error("Failed to pull no course_name given and not in a git repo")
            return (None, None)
    folder_name = output
    if not output:
        folder_name = course_name

    git_url = f"{base_url}{course_name}.git"
    return (git_url, folder_name)


def get_repo(folder_name):
    base_url = os.getenv("GIT_URL")
    if not base_url:
        log.error("Missing env var GIT_URL")
        return None
    try:
        git_repo = git.Repo(folder_name)
        git_url = git_repo.remote().url
        if not git_url.startswith(base_url):
            log.error(
                f"Invalid git remote_url {git_url} needs to start with {base_url}")
            return None
        return git_repo
    except Exception:
        log.error(f"{folder_name} is not a git repo")
        return None


def reset_deleted_files(repo):
    branch = repo.active_branch
    deleted_files = repo.git.ls_files("--deleted").split('\n')
    deleted_upstream = get_upstream_deleted(repo)
    for filename in deleted_files:
        if (not filename):
            continue
        if (filename in deleted_upstream):
            repo.git.checkout("HEAD", '--', filename)
        else:
            log.dbg(f"Restoring deleted file {filename}")
            repo.git.checkout(f"origin/{branch}", "--", filename)


def merge(repo):
    branch = repo.active_branch
    remote_branch = f"origin/{branch}"
    repo.git.merge("-Xours", remote_branch)


def pull_admin(course_name, output, branch, depth):
    isAdmin = os.getenv('IS_ADMIN') == "1"
    if (not isAdmin):
        return
    if (not course_name):
        return
    base_url = os.getenv("GIT_URL_ADMIN")
    if (not base_url):
        log.error("Missing env var GIT_URL_ADMIN")
        return
    git_url = f"{base_url}{course_name}.git"
    folder_name = output
    if not output:
        folder_name = f"{course_name}-admin"

    if (not os.path.exists(folder_name)):
        log.log(f"{folder_name} does not exists. Cloning repo {git_url}")
        try:
            git.Repo.clone_from(git_url, folder_name,
                                branch=branch, depth=depth)
            log.log("Cloned successfully.")
        except Exception as err:
            log.error("Failed to clone repo. Error:")
            log.error(err)
    else:
        log.log(f"{folder_name} exists.")
        try:
            repo = git.Repo(folder_name)
            log.log(f"Pulling...")
            repo.git.pull("--ff-only")
            log.log(f"Done")

        except Exception as err:
            log.error(f"{folder_name} is not a git repo")


@click.command(help="Pull the course repo")
@click.argument(
    "course_name",
    type=str,
    default=None,
    required=False
)
@click.option("-o", "--output", type=str, help="The name of the output folder", default=None)
@click.option("-b", "--branch", help="The branch to pull", default="main")
@click.option("-d", "--depth", help="The depth for git fetch", default=1)
@click.pass_context
def pull(ctx, course_name, output, branch, depth):
    log.setup_logger()
    try:
        pull_fn(ctx, course_name, output, branch, depth)
    except Exception as err:
        log.error("pull failed with unexpected error.")
        log.error(err)


def pull_fn(ctx, course_name, output, branch, depth):
    pull_admin(course_name, output, branch, depth)

    (git_url, folder_name) = get_course_info(course_name, output)
    if (not git_url or not folder_name):
        return

    if (not os.path.exists(folder_name)):
        log.log(f"{folder_name} does not exists. Cloning repo {git_url}")
        try:
            git.Repo.clone_from(git_url, folder_name,
                                branch=branch, depth=depth)
            log.log("Cloned successfully.")
        except Exception as err:
            log.error("Failed to clone repo. Error:")
            log.error(err)
        return
    log.log(f"{folder_name} exists. Updating the repo")
    repo = get_repo(folder_name)
    if (not repo):
        return

    log.log(f"Fetching changes...")
    repo.remote().fetch()
    log.log(f"Restoring locally deleted files")
    reset_deleted_files(repo)
    log.log(f"unstaging all changes")
    repo.git.reset("--mixed")

    util.update_repo_config(repo)
    if repo.is_dirty():
        log.log("Repo is dirty. Commiting....")
        repo.git.commit("-am", "Automatic commit by urnc",
                        "--allow-empty")
        log.log("Created new commit")

    log.log("Merging from remote...")
    merge(repo)
    log.log("Done.")
