#pragma once

#include "models/repository.h"
#include <optional>
#include <string>
#include <vector>

struct GitAuthOverride
{
    std::string remote_url;
    std::string username;
    std::string password;
};

class GitManager
{
public:
    GitManager();
    ~GitManager();

    bool openRepositoryHandle(RepositoryView &repository, const std::string &path, std::string &error);
    bool openRepository(RepositoryView &repository, const std::string &path, std::string &error);
    bool initializeRepository(RepositoryView &repository, const std::string &path,
                              const std::string &initial_branch, std::string &error);
    bool cloneRepository(RepositoryView &repository, const std::string &url,
                         const std::string &path, bool shallow, std::string &error);
    bool reload(RepositoryView &repository, std::string &error);
    bool loadMoreCommits(RepositoryView &repository, size_t page_size, std::string &error);
    bool checkoutBranch(RepositoryView &repository, const std::string &branch, std::string &error);
    bool mergeBranch(RepositoryView &repository, const std::string &source_branch,
                     const std::string &target_branch, std::string &error);
    bool fetch(RepositoryView &repository, const std::string &remote, std::string &error,
               const std::optional<GitAuthOverride> &auth = std::nullopt);
    bool pull(RepositoryView &repository, int mode, std::string &error,
              const std::optional<GitAuthOverride> &auth = std::nullopt);
    bool push(RepositoryView &repository, std::string &error,
              const std::optional<GitAuthOverride> &auth = std::nullopt);
    bool pushBranch(RepositoryView &repository, const std::string &branch, std::string &error,
                    const std::optional<GitAuthOverride> &auth = std::nullopt);
    bool stash(RepositoryView &repository, std::string &error);
    bool popStash(RepositoryView &repository, std::string &error);
    bool undoCommit(RepositoryView &repository, std::string &error);
    bool redoCommit(RepositoryView &repository, std::string &error);
    bool stageAll(RepositoryView &repository, std::string &error);
    bool unstageAll(RepositoryView &repository, std::string &error);
    bool stageFile(RepositoryView &repository, const std::string &path, std::string &error);
    bool unstageFile(RepositoryView &repository, const std::string &path, std::string &error);
    bool discardFile(RepositoryView &repository, const std::string &path, std::string &error);
    bool discardAll(RepositoryView &repository, std::string &error);
    bool commit(RepositoryView &repository, const std::string &summary,
                const std::string &description, bool amend, std::string &error);
    bool createBranch(RepositoryView &repository, const std::string &name,
                      const std::string &start_point, bool checkout, std::string &error);
    bool createTag(RepositoryView &repository, const std::string &name,
                   const std::string &target, std::string &error);
    bool addRemote(RepositoryView &repository, const std::string &name,
                   const std::string &url, std::string &error);
    bool addWorktree(RepositoryView &repository, const std::string &path,
                     const std::string &branch, std::string &error);
    bool addSubmodule(RepositoryView &repository, const std::string &url,
                      const std::string &path, std::string &error);
    bool updateSubmodule(RepositoryView &repository, const std::string &name, std::string &error);
    std::string worktreePath(RepositoryView &repository, const std::string &name, std::string &error);
    bool loadCommitChanges(RepositoryView &repository, int commit_index, std::string &error);
    bool loadCommitFiles(RepositoryView &repository, int commit_index, std::string &error);
    bool captureGit(RepositoryView &repository, const std::vector<std::string> &arguments,
                    std::string &output, std::string &error,
                    const std::string *stdin_data = nullptr);
    bool applyPatch(RepositoryView &repository, const std::string &patch, bool cached,
                    bool reverse, std::string &error);
    std::string remoteUrl(RepositoryView &repository, const std::string &remote_name, std::string &error);
    bool storeCredential(RepositoryView &repository, const std::string &remote_url,
                         const std::string &username, const std::string &password,
                         std::string &error);
    static bool isAuthenticationFailure(std::string_view message);

private:
    static std::string lastError(const char *fallback);
    static std::string formatTime(git_time_t value, int offset_minutes);
    void loadToolbarHistoryActions(RepositoryView &repository);
    void readBranches(RepositoryView &repository, git_branch_t type, std::vector<std::string> &output);
    void loadBranchDivergence(RepositoryView &repository);
    void loadRefBadges(RepositoryView &repository);
    void computeGraphLanes(RepositoryView &repository);
    bool loadRepositoryData(RepositoryView &repository, std::string &error);
    void loadWorkingTree(RepositoryView &repository);
    bool prepareCredentials(RepositoryView &repository, std::string &error);
    bool runGit(RepositoryView &repository, const std::vector<std::string> &arguments,
                const char *success_message, std::string &message, bool reload = true,
                const std::optional<GitAuthOverride> &auth = std::nullopt);
};
