//! Rhai wrappers for Git module functions
//!
//! This module provides Rhai wrappers for the functions in the Git module.

use super::{parse_git_url_extended, GitError, GitRepo, GitTree, ParsedGitUrl};
use rhai::{Array, Dynamic, Engine, EvalAltResult};

/// Register Git module functions with the Rhai engine
///
/// # Arguments
///
/// * `engine` - The Rhai engine to register the functions with
///
/// # Returns
///
/// * `Result<(), Box<EvalAltResult>>` - Ok if registration was successful, Err otherwise
pub fn register_git_module(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> {
    // Register GitTree constructor
    engine.register_type::<GitTree>();
    engine.register_fn("git_tree_new", git_tree_new);

    // Register GitTree methods
    engine.register_fn("list", git_tree_list);
    engine.register_fn("find", git_tree_find);
    engine.register_fn("get", git_tree_get);
    engine.register_fn("get_path", git_tree_get_path);
    engine.register_fn("get_path_reset", git_tree_get_path_reset);
    engine.register_fn("get_path_pull", git_tree_get_path_pull);
    engine.register_fn("clone_repo", git_tree_clone_repo);
    engine.register_fn("clone_repo_reset", git_tree_clone_repo_reset);
    engine.register_fn("clone_repo_pull", git_tree_clone_repo_pull);
    engine.register_fn("clone_repo_branch", git_tree_clone_repo_branch);
    engine.register_fn("clone_repo_full", git_tree_clone_repo_full);

    // Register ParsedGitUrl type and accessor methods
    engine.register_type::<ParsedGitUrl>();
    engine.register_fn("parse_git_url_extended", rhai_parse_git_url_extended);
    engine.register_get("server", |p: &mut ParsedGitUrl| p.server.clone());
    engine.register_get("account", |p: &mut ParsedGitUrl| p.account.clone());
    engine.register_get("repo", |p: &mut ParsedGitUrl| p.repo.clone());
    engine.register_get("branch", |p: &mut ParsedGitUrl| {
        p.branch.clone().unwrap_or_default()
    });
    engine.register_get("subpath", |p: &mut ParsedGitUrl| {
        p.subpath.clone().unwrap_or_default()
    });
    engine.register_fn("clone_url", |p: &mut ParsedGitUrl| p.clone_url());
    engine.register_fn("is_valid", |p: &mut ParsedGitUrl| p.is_valid());

    // Register GitRepo methods
    engine.register_type::<GitRepo>();
    engine.register_fn("path", git_repo_path);
    engine.register_fn("has_changes", git_repo_has_changes);
    engine.register_fn("pull", git_repo_pull);
    engine.register_fn("reset", git_repo_reset);
    engine.register_fn("commit", git_repo_commit);
    engine.register_fn("push", git_repo_push);

    // Register git_clone function for testing
    engine.register_fn("git_clone", git_clone);

    Ok(())
}

// Helper functions for error conversion
fn git_error_to_rhai_error<T>(result: Result<T, GitError>) -> Result<T, Box<EvalAltResult>> {
    result.map_err(|e| {
        Box::new(EvalAltResult::ErrorRuntime(
            format!("Git error: {}", e).into(),
            rhai::Position::NONE,
        ))
    })
}

//
// GitTree Function Wrappers
//

/// Wrapper for GitTree::new
///
/// Creates a new GitTree with the specified base path.
pub fn git_tree_new(base_path: &str) -> Result<GitTree, Box<EvalAltResult>> {
    git_error_to_rhai_error(GitTree::new(base_path))
}

/// Wrapper for GitTree::list
///
/// Lists all git repositories under the base path.
pub fn git_tree_list(git_tree: &mut GitTree) -> Result<Array, Box<EvalAltResult>> {
    let repos = git_error_to_rhai_error(git_tree.list())?;

    // Convert Vec<String> to Rhai Array
    let mut array = Array::new();
    for repo in repos {
        array.push(Dynamic::from(repo));
    }

    Ok(array)
}

/// Wrapper for GitTree::find
///
/// Finds repositories matching a pattern and returns them as an array of GitRepo objects.
/// Assumes the underlying GitTree::find Rust method now returns Result<Vec<GitRepo>, GitError>.
pub fn git_tree_find(git_tree: &mut GitTree, pattern: &str) -> Result<Array, Box<EvalAltResult>> {
    let repos: Vec<GitRepo> = git_error_to_rhai_error(git_tree.find(pattern))?;

    // Convert Vec<GitRepo> to Rhai Array
    let mut array = Array::new();
    for repo in repos {
        array.push(Dynamic::from(repo));
    }

    Ok(array)
}

/// Wrapper for GitTree::get
///
/// Gets a single GitRepo object based on an exact name or URL.
/// The underlying Rust GitTree::get method returns Result<Vec<GitRepo>, GitError>.
/// This wrapper ensures that for Rhai, 'get' returns a single GitRepo or an error
/// if zero or multiple repositories are found (for local names/patterns),
/// or if a URL operation fails or unexpectedly yields not exactly one result.
pub fn git_tree_get(
    git_tree: &mut GitTree,
    name_or_url: &str,
) -> Result<GitRepo, Box<EvalAltResult>> {
    let mut repos_vec: Vec<GitRepo> = git_error_to_rhai_error(git_tree.get(name_or_url))?;

    match repos_vec.len() {
        1 => Ok(repos_vec.remove(0)), // Efficient for Vec of size 1, transfers ownership
        0 => Err(Box::new(EvalAltResult::ErrorRuntime(
            format!("Git error: Repository '{}' not found.", name_or_url).into(),
            rhai::Position::NONE,
        ))),
        _ => Err(Box::new(EvalAltResult::ErrorRuntime(
            format!(
                "Git error: Multiple repositories ({}) found matching '{}'. Use find() for patterns or provide a more specific name for get().",
                repos_vec.len(),
                name_or_url
            )
            .into(),
            rhai::Position::NONE,
        ))),
    }
}

/// Wrapper for GitTree::get_path
///
/// Clones a repository and returns the path directly.
/// Supports extended URL formats with branch and subpath.
///
/// # Example (Rhai)
/// ```rhai
/// let git_tree = git_tree_new("/tmp/repos");
/// let docs_path = git_tree.get_path("https://forge.ourworld.tf/org/repo/src/branch/main/docs");
/// ```
pub fn git_tree_get_path(git_tree: &mut GitTree, url: &str) -> Result<String, Box<EvalAltResult>> {
    git_error_to_rhai_error(git_tree.get_path(url))
}

/// Wrapper for GitTree::get_path with reset
///
/// Clones a repository, resets any local changes, and returns the path directly.
/// Supports extended URL formats with branch and subpath.
///
/// # Example (Rhai)
/// ```rhai
/// let git_tree = git_tree_new("/tmp/repos");
/// let docs_path = git_tree.get_path_reset("https://forge.ourworld.tf/org/repo/src/branch/main/docs");
/// ```
pub fn git_tree_get_path_reset(
    git_tree: &mut GitTree,
    url: &str,
) -> Result<String, Box<EvalAltResult>> {
    git_error_to_rhai_error(git_tree.clone_repo(url).reset().execute())
}

/// Wrapper for GitTree::get_path with reset and pull
///
/// Clones a repository, resets any local changes, pulls latest, and returns the path.
/// Supports extended URL formats with branch and subpath.
///
/// # Example (Rhai)
/// ```rhai
/// let git_tree = git_tree_new("/tmp/repos");
/// let docs_path = git_tree.get_path_pull("https://forge.ourworld.tf/org/repo/src/branch/main/docs");
/// ```
pub fn git_tree_get_path_pull(
    git_tree: &mut GitTree,
    url: &str,
) -> Result<String, Box<EvalAltResult>> {
    git_error_to_rhai_error(git_tree.clone_repo(url).reset().pull().execute())
}

/// Wrapper for GitTree::clone_repo with just URL
///
/// Clones a repository using the builder pattern (simple version).
pub fn git_tree_clone_repo(
    git_tree: &mut GitTree,
    url: &str,
) -> Result<String, Box<EvalAltResult>> {
    git_error_to_rhai_error(git_tree.clone_repo(url).execute())
}

/// Wrapper for GitTree::clone_repo with reset
///
/// Clones a repository and resets any local changes.
pub fn git_tree_clone_repo_reset(
    git_tree: &mut GitTree,
    url: &str,
) -> Result<String, Box<EvalAltResult>> {
    git_error_to_rhai_error(git_tree.clone_repo(url).reset().execute())
}

/// Wrapper for GitTree::clone_repo with pull
///
/// Clones a repository and pulls latest changes.
pub fn git_tree_clone_repo_pull(
    git_tree: &mut GitTree,
    url: &str,
) -> Result<String, Box<EvalAltResult>> {
    git_error_to_rhai_error(git_tree.clone_repo(url).reset().pull().execute())
}

/// Wrapper for GitTree::clone_repo with branch
///
/// Clones a repository with a specific branch.
pub fn git_tree_clone_repo_branch(
    git_tree: &mut GitTree,
    url: &str,
    branch: &str,
) -> Result<String, Box<EvalAltResult>> {
    git_error_to_rhai_error(git_tree.clone_repo(url).branch(branch).execute())
}

/// Wrapper for GitTree::clone_repo with all options
///
/// Clones a repository with branch, subpath, reset, and pull options.
///
/// # Arguments
/// * `url` - The repository URL
/// * `branch` - The branch to checkout (empty string to use URL default)
/// * `subpath` - The subpath to return (empty string for repo root)
/// * `reset` - Whether to reset local changes
/// * `pull` - Whether to pull latest changes
pub fn git_tree_clone_repo_full(
    git_tree: &mut GitTree,
    url: &str,
    branch: &str,
    subpath: &str,
    reset: bool,
    pull: bool,
) -> Result<String, Box<EvalAltResult>> {
    let mut builder = git_tree.clone_repo(url);

    if !branch.is_empty() {
        builder = builder.branch(branch);
    }
    if !subpath.is_empty() {
        builder = builder.subpath(subpath);
    }
    if reset {
        builder = builder.reset();
    }
    if pull {
        builder = builder.pull();
    }

    git_error_to_rhai_error(builder.execute())
}

/// Wrapper for parse_git_url_extended
///
/// Parses an extended git URL and returns a ParsedGitUrl object.
pub fn rhai_parse_git_url_extended(url: &str) -> ParsedGitUrl {
    parse_git_url_extended(url)
}

//
// GitRepo Function Wrappers
//

/// Wrapper for GitRepo::path
///
/// Gets the path of the repository.
pub fn git_repo_path(git_repo: &mut GitRepo) -> String {
    git_repo.path().to_string()
}

/// Wrapper for GitRepo::has_changes
///
/// Checks if the repository has uncommitted changes.
pub fn git_repo_has_changes(git_repo: &mut GitRepo) -> Result<bool, Box<EvalAltResult>> {
    git_error_to_rhai_error(git_repo.has_changes())
}

/// Wrapper for GitRepo::pull
///
/// Pulls the latest changes from the remote repository.
pub fn git_repo_pull(git_repo: &mut GitRepo) -> Result<GitRepo, Box<EvalAltResult>> {
    git_error_to_rhai_error(git_repo.pull())
}

/// Wrapper for GitRepo::reset
///
/// Resets any local changes in the repository.
pub fn git_repo_reset(git_repo: &mut GitRepo) -> Result<GitRepo, Box<EvalAltResult>> {
    git_error_to_rhai_error(git_repo.reset())
}

/// Wrapper for GitRepo::commit
///
/// Commits changes in the repository.
pub fn git_repo_commit(
    git_repo: &mut GitRepo,
    message: &str,
) -> Result<GitRepo, Box<EvalAltResult>> {
    git_error_to_rhai_error(git_repo.commit(message))
}

/// Wrapper for GitRepo::push
///
/// Pushes changes to the remote repository.
pub fn git_repo_push(git_repo: &mut GitRepo) -> Result<GitRepo, Box<EvalAltResult>> {
    git_error_to_rhai_error(git_repo.push())
}

/// Clone a git repository to a temporary location
///
/// This function clones a repository from the given URL to a temporary directory
/// and returns the GitRepo object for further operations.
///
/// # Arguments
///
/// * `url` - The URL of the git repository to clone
///
/// # Returns
///
/// * `Ok(GitRepo)` - The cloned repository object
/// * `Err(Box<EvalAltResult>)` - If the clone operation failed
pub fn git_clone(url: &str) -> Result<GitRepo, Box<EvalAltResult>> {
    // Get base path from environment or use default temp directory
    let base_path = std::env::var("GIT_DEFAULT_BASE_PATH").unwrap_or_else(|_| {
        std::env::temp_dir()
            .join("herosal_git_clones")
            .to_string_lossy()
            .to_string()
    });

    // Create GitTree and clone the repository
    let git_tree = git_error_to_rhai_error(GitTree::new(&base_path))?;
    let repos = git_error_to_rhai_error(git_tree.get(url))?;

    // Return the first (and should be only) repository
    repos.into_iter().next().ok_or_else(|| {
        Box::new(EvalAltResult::ErrorRuntime(
            "Git error: No repository was cloned".into(),
            rhai::Position::NONE,
        ))
    })
}
