import json
import os
import shutil
import subprocess
import sys
import typer
import git
from rich.progress import Progress, SpinnerColumn, TextColumn
from rich.console import Console
from typing import Optional
from typing_extensions import Annotated

app = typer.Typer()
err_console = Console(stderr=True)
ABS_PATH = os.path.dirname(__file__)


def install_req(repo_path: str, archive: str = "requirements.txt"):
    """Install dependencies from requirements.txt"""
    archive_abs_path = os.path.join(str(repo_path), archive)
    if os.path.exists(archive_abs_path):
        print("Installing dependencies ", end=" ", flush=True)
        try:
            subprocess.run([sys.executable, "-m", "pip", "install", "-r", archive_abs_path], check=True)
            print("[OK]")
        except subprocess.CalledProcessError:
            print("[ERROR]")
    else:
        print("No dependency file found [OK]")


def remove_garbage(repo_path: str):
    """Remove unnecessary files"""
    print("Removing unnecessary files ", end=" ", flush=True)
    try:
        if not os.path.exists(str(repo_path)):
            print("[ERROR: Path not found]")
            return
            
        items = os.listdir(repo_path)
        for item in items:
            item_path = os.path.join(repo_path, item)
            if os.path.isfile(item_path):
                os.remove(item_path)
        
        # Remove .git and setup folders if they exist
        for folder in [".git", "setup"]:
            folder_path = os.path.join(repo_path, folder)
            if os.path.exists(folder_path) and os.path.isdir(folder_path):
                shutil.rmtree(folder_path)
        print("[OK]")
    except Exception as e:
        print(f"[ERROR]: {e}")
        sys.exit(1)


def move_folders(repo_path: str):
    """Move folders to current directory"""
    print("Moving repository to current directory ", end=" ", flush=True)
    try:
        if not os.path.exists(str(repo_path)):
            print("[ERROR: Path not found]")
            return
            
        items = os.listdir(repo_path)
        for item in items:
            src = os.path.join(repo_path, item)
            dst = os.path.join(os.getcwd(), item)
            if os.path.exists(dst):
                if os.path.isdir(dst):
                    shutil.rmtree(dst)
                else:
                    os.remove(dst)
            shutil.move(src, dst)
        shutil.rmtree(repo_path)
        print("[OK]")
    except Exception as e:
        print(f"[ERROR]: {e}")
        sys.exit(1)

def git_clone_update(url: str, branch: str, token: str = None, repo_path: str = None):
    """Clones or updates a Git repository.
    If there are local changes, it performs a hard reset and pulls.
    """
    url_old = url

    if not repo_path:
        if ".git" in url:
            repo_path = url.split(".git")[0].split("/")[-1]
        else:
            repo_path = url.split("/")[-1]

    if token:
        if "github.com" in url:
            url = url.replace("github.com", f"{token}@github.com")
        elif "gitlab.com" in url:
            url = url.replace("gitlab.com", f"oauth2:{token}@gitlab.com")
        
        if not url.endswith(".git"):
            url += ".git"

    is_update = os.path.exists(str(repo_path)) and os.path.exists(os.path.join(str(repo_path), ".git"))
    
    if is_update:
        message = f"[cyan] Updating {repo_path}... [/cyan]"
    else:
        message = f"[cyan] Cloning {url_old} ({branch})... [/cyan]"

    with Progress(SpinnerColumn(), TextColumn("{task.description}")) as progress:
        task = progress.add_task(description=message, total=None)

        try:
            if is_update:
                repo = git.Repo(repo_path)
                repo.git.update_environment(GIT_TERMINAL_PROMPT="0")
                repo.git.reset("--hard")
                repo.git.pull()
            else:
                git.Repo.clone_from(url, repo_path, branch=branch, recursive=True, env={"GIT_TERMINAL_PROMPT": "0"})

            progress.update(task, description=f"[green]{message} ✔[/green]", completed=1)
            return repo_path

        except Exception as e:
            progress.update(task, description=f"[red]Error in {repo_path} ✖[/red]", completed=1)
            print(f"\n[red]Git Error:[/red] {e}")
            sys.exit(1)


def git_update_all():
    """Update all repositories in the current directory"""
    with Progress(SpinnerColumn(), TextColumn("{task.description}")) as progress:
        for carpeta in os.listdir():
            ruta_completa = os.path.join(os.getcwd(), carpeta)
            if os.path.isdir(ruta_completa) and os.path.isdir(os.path.join(ruta_completa, ".git")):
                try:
                    message = f"[cyan] Updating {carpeta}... [/cyan]"
                    task = progress.add_task(description=message, total=None)
                    
                    repo = git.Repo(ruta_completa)
                    repo.git.update_environment(GIT_TERMINAL_PROMPT="0")
                    repo.git.pull()
                    
                    progress.update(task, description=f"[green]{message} ✔[/green]", completed=1)
                except Exception:
                    progress.update(task, description=f"[red]Error in {carpeta} ✖[/red]", completed=1)

@app.command()
def main(
    url: Annotated[Optional[str], typer.Argument(help="Repository URL")]=None,
    repositories: Annotated[
    Optional[str], typer.Option(help="JSON with repositories to clone"),
    ] = None,
    branch: Annotated[Optional[str], typer.Option(help="Repository branch")] = None,
    requirements : Annotated[
        Optional[bool], typer.Option(help="Do not install dependencies"),
    ] = False,
    # update: Annotated[
    #     Optional[bool], typer.Option(help="Update all repositories"),
    # ] = False,
    # garbage: Annotated[
    #     Optional[bool], typer.Option(help="Do not remove unnecessary files"),
    # ] = True,
    repopath: Annotated[
        Optional[str], typer.Option(help="Repository path"),
    ] = None,
    token: Annotated[
        Optional[str], typer.Option(help="Repository access token"),
    ] = None,
):

    """Utility for cloning Git repositories, removing unnecessary files, and installing dependencies from requirements.txt."""

    # if update:
    #     git_update_all()
    #     sys.exit(0)

    if not repositories:
        """Simple cloning of a repository"""
        repo_path_actual = git_clone_update(url=url, branch=branch, token=token, repo_path=repopath)

        if requirements:
            install_req(repo_path=repo_path_actual)
        # if garbage:
        #     remove_garbage(repo_path=repo_path_actual)
        # if not repopath:
        #     move_folders(repo_path=repo_path_actual)
    
    if repositories:
        """
        Cloning multiple repositories
        JSON Format:
        [
            {
                "url": "https://github.com/org/repo1",
                "branch": "18.0",
                "repo_path": "repo1",
                "token": "token1"
            },
            {
                "url": "https://github.com/OCA/commission",
                "branch": "18.0",
                "repo_path": "oca/commission"
            }
        ]
        """
        for repo in json.loads(repositories):
            current_branch = branch if branch else repo.get("branch")
            if not current_branch:
                err_console.print("✖[red] Branch not defined [/red]")
                raise typer.Exit(1)
            repo_path = repo.get("repo_path")
            current_token = token if token else repo.get("token")
            git_clone_update(url=repo["url"], branch=current_branch, token=current_token, repo_path=repo_path)
        

if __name__ == "__main__":
    app()
