import { tracedInvoke as invoke } from "./telemetry";

import type {
  AiReviewResult,
  AiCommitPlan,
  CommitAiProvider,
  CloneOptions,
  ConflictFile,
  DetectedExternalTool,
  ExternalToolCommand,
  ExternalDiffScope,
  GitBlameResult,
  GitBranch,
  GitCommit,
  GitCommitComparison,
  GitIgnoreKind,
  GitIntegrationProvider,
  GitIntegrationRepository,
  GitLfsStatus,
  GitRepositoryFile,
  GitRemote,
  MergeStrategy,
  PullStrategy,
  RebaseCommit,
  RebasePlanItem,
  ReflogEntry,
  GitSearchHit,
  GitStash,
  GitStatus,
  GitTag,
  GitWorktree,
  PatchApplyAction,
  RepositoryBundle,
  StoredCredential,
} from "./types";

export function openRepository(path: string): Promise<GitStatus> {
  return invoke<GitStatus>("open_repository", { path });
}

export function initRepository(path: string, initialBranch = "main"): Promise<GitStatus> {
  return invoke<GitStatus>("init_repository", { path, initialBranch });
}

export function openRepoInExplorer(path: string): Promise<void> {
  return invoke<void>("open_repo_in_explorer", { path });
}

export function openRepositoryFile(path: string, file: string): Promise<void> {
  return invoke<void>("open_repository_file", { path, file });
}

export function detectExternalTools(): Promise<DetectedExternalTool[]> {
  return invoke<DetectedExternalTool[]>("detect_external_tools");
}

export function listIntegrationRepositories(provider: GitIntegrationProvider, baseUrl: string, accountId?: string): Promise<GitIntegrationRepository[]> {
  return invoke<GitIntegrationRepository[]>("list_integration_repositories", { provider, baseUrl, accountId: accountId ?? null });
}

export function launchExternalTool(path: string, command: ExternalToolCommand, file?: string): Promise<void> {
  return invoke<void>("launch_external_tool", { path, file: file ?? null, command });
}

export function launchExternalDiff(path: string, file: string, command: ExternalToolCommand, scope: ExternalDiffScope = "head"): Promise<void> {
  return invoke<void>("launch_external_diff", { path, file, command, scope });
}

export function launchExternalMerge(path: string, file: string, command: ExternalToolCommand): Promise<void> {
  return invoke<void>("launch_external_merge", { path, file, command });
}

export function openRepositoryBundle(path: string, commitLimit = 100): Promise<RepositoryBundle> {
  return invoke<RepositoryBundle>("open_repository_bundle", { path, commitLimit });
}

export function cloneRepository(
  remoteUrl: string,
  parentPath: string,
  directoryName?: string,
  username?: string,
  password?: string,
  commitLimit = 100,
  options: CloneOptions = { branch: null, blobless: false, customFlags: "", shallowDepth: null, shallowSince: null, sparse: false, sparsePaths: [] },
): Promise<RepositoryBundle> {
  return invoke<RepositoryBundle>("clone_repository", {
    remoteUrl,
    parentPath,
    directoryName: directoryName?.trim() ? directoryName.trim() : null,
    username: username ?? null,
    password: password ?? null,
    commitLimit,
    branch: options.branch,
    blobless: options.blobless,
    customFlags: options.customFlags,
    shallowDepth: options.shallowDepth,
    shallowSince: options.shallowSince,
    sparse: options.sparse,
    sparsePaths: options.sparsePaths,
  });
}

export function getStatus(path: string): Promise<GitStatus> {
  return invoke<GitStatus>("get_status", { path });
}

export function getGitLfsStatus(path: string): Promise<GitLfsStatus> {
  return invoke<GitLfsStatus>("git_lfs_status", { path });
}

export function installGitLfs(path: string): Promise<GitLfsStatus> {
  return invoke<GitLfsStatus>("git_lfs_install", { path });
}

export function trackGitLfsPattern(path: string, pattern: string, lockable = false): Promise<GitLfsStatus> {
  return invoke<GitLfsStatus>("git_lfs_track", { path, pattern, lockable });
}

export function untrackGitLfsPattern(path: string, pattern: string): Promise<GitLfsStatus> {
  return invoke<GitLfsStatus>("git_lfs_untrack", { path, pattern });
}

export function pullGitLfsObjects(path: string, remote?: string, username?: string, password?: string): Promise<GitLfsStatus> {
  return invoke<GitLfsStatus>("git_lfs_pull", {
    path,
    remote: remote || null,
    username: username ?? null,
    password: password ?? null,
  });
}

export function pruneGitLfsObjects(path: string): Promise<GitLfsStatus> {
  return invoke<GitLfsStatus>("git_lfs_prune", { path });
}

// Sets the taskbar icon badge to ahead + behind + changed status files (0 clears it). Windows only — a no-op on
// other platforms, since Windows has no native numeric badge to fall back to.
export function setSyncBadge(ahead: number, behind: number, changes: number): Promise<void> {
  return invoke<void>("set_sync_badge", { ahead, behind, changes });
}

export function listBranches(path: string): Promise<GitBranch[]> {
  return invoke<GitBranch[]>("list_branches", { path });
}

export function listRemotes(path: string): Promise<GitRemote[]> { return invoke("list_remotes", { path }); }
export function addRemote(path: string, name: string, url: string): Promise<GitRemote[]> { return invoke("add_remote", { path, name, url }); }
export function updateRemote(path: string, name: string, url: string): Promise<GitRemote[]> { return invoke("update_remote", { path, name, url }); }
export function removeRemote(path: string, name: string): Promise<GitRemote[]> {
  console.log("remove_remote")
  return invoke("remove_remote", { path, name }); }
export function setBranchUpstream(path: string, branch: string, upstream?: string): Promise<GitStatus> { return invoke("set_branch_upstream", { path, branch, upstream: upstream || null }); }
export function deleteRemoteBranch(path: string, remote: string, branch: string, username?: string, password?: string): Promise<GitStatus> {
  return invoke("delete_remote_branch", { path, remote, branch, username: username ?? null, password: password ?? null });
}
export function deleteRemoteBranches(path: string, remote: string, branches: string[], username?: string, password?: string): Promise<GitStatus> {
  return invoke("delete_remote_branches", { path, remote, branches, username: username ?? null, password: password ?? null });
}

export function listStashes(path: string): Promise<GitStash[]> {
  return invoke<GitStash[]>("list_stashes", { path });
}

export function checkoutBranch(path: string, branch: string): Promise<GitStatus> {
  return invoke<GitStatus>("checkout_branch", { path, branch });
}

export function createBranch(
  path: string,
  branch: string,
  startPoint?: string,
): Promise<GitStatus> {
  return invoke<GitStatus>("create_branch", { path, branch, startPoint: startPoint ?? null });
}

export function renameBranch(
  path: string,
  oldBranch: string,
  newBranch: string,
): Promise<GitStatus> {
  return invoke<GitStatus>("rename_branch", { path, oldBranch, newBranch });
}

export function renameRemoteBranch(
  path: string,
  remote: string,
  oldBranch: string,
  newBranch: string,
  username?: string,
  password?: string,
): Promise<GitStatus> {
  return invoke<GitStatus>("rename_remote_branch", {
    path,
    remote,
    oldBranch,
    newBranch,
    username: username ?? null,
    password: password ?? null,
  });
}

export function deleteBranch(path: string, branch: string, force = false): Promise<GitStatus> {
  return invoke<GitStatus>("delete_branch", { path, branch, force });
}

export function listWorktrees(path: string): Promise<GitWorktree[]> {
  return invoke<GitWorktree[]>("list_worktrees", { path });
}

export function addWorktree(
  path: string,
  worktreePath: string,
  options: {
    branch?: string;
    newBranch?: string;
    startPoint?: string;
    detached?: boolean;
    lock?: boolean;
  } = {},
): Promise<GitWorktree[]> {
  return invoke<GitWorktree[]>("add_worktree", {
    path,
    worktreePath,
    branch: options.branch ?? null,
    newBranch: options.newBranch ?? null,
    startPoint: options.startPoint ?? null,
    detached: options.detached ?? false,
    lock: options.lock ?? false,
  });
}

export function removeWorktree(path: string, worktreePath: string, force = false): Promise<GitWorktree[]> {
  return invoke<GitWorktree[]>("remove_worktree", { path, worktreePath, force });
}

export function moveWorktree(path: string, worktreePath: string, destination: string): Promise<GitWorktree[]> {
  return invoke<GitWorktree[]>("move_worktree", { path, worktreePath, destination });
}

export function lockWorktree(path: string, worktreePath: string, reason?: string): Promise<GitWorktree[]> {
  return invoke<GitWorktree[]>("lock_worktree", { path, worktreePath, reason: reason?.trim() || null });
}

export function unlockWorktree(path: string, worktreePath: string): Promise<GitWorktree[]> {
  return invoke<GitWorktree[]>("unlock_worktree", { path, worktreePath });
}

export function pruneWorktrees(path: string): Promise<GitWorktree[]> {
  return invoke<GitWorktree[]>("prune_worktrees", { path });
}

export function repairWorktree(path: string, worktreePath: string): Promise<GitWorktree[]> {
  return invoke<GitWorktree[]>("repair_worktree", { path, worktreePath });
}

export function listTags(path: string): Promise<GitTag[]> {
  return invoke<GitTag[]>("list_tags", { path });
}

export function createTag(
  path: string,
  name: string,
  target?: string,
  message?: string,
): Promise<GitTag[]> {
  return invoke<GitTag[]>("create_tag", {
    path,
    name,
    target: target?.trim() ? target.trim() : null,
    message: message?.trim() ? message.trim() : null,
  });
}

export function deleteTag(path: string, name: string): Promise<GitTag[]> {
  return invoke<GitTag[]>("delete_tag", { path, name });
}

export function pushTag(
  path: string,
  name: string,
  username?: string,
  password?: string,
): Promise<void> {
  return invoke<void>("push_tag", { path, name, username: username ?? null, password: password ?? null });
}

export function cherryPickCommit(path: string, commit: string): Promise<GitStatus> {
  return invoke<GitStatus>("cherry_pick_commit", { path, commit });
}

export function cherryPickContinue(path: string): Promise<GitStatus> {
  return invoke<GitStatus>("cherry_pick_continue", { path });
}

export function cherryPickAbort(path: string): Promise<GitStatus> {
  return invoke<GitStatus>("cherry_pick_abort", { path });
}

export function stageFiles(path: string, files: string[]): Promise<GitStatus> {
  return invoke<GitStatus>("stage_files", { path, files });
}

export function unstageFiles(path: string, files: string[]): Promise<GitStatus> {
  return invoke<GitStatus>("unstage_files", { path, files });
}

export function addToGitignore(path: string, target: string, kind: GitIgnoreKind): Promise<GitStatus> {
  return invoke<GitStatus>("add_to_gitignore", { path, target, kind });
}

export function untrackPaths(path: string, targets: string[]): Promise<GitStatus> {
  return invoke<GitStatus>("untrack_paths", { path, targets });
}

export function restoreFiles(
  path: string,
  files: string[],
  staged: boolean,
): Promise<GitStatus> {
  return invoke<GitStatus>("restore_files", { path, files, staged });
}

export function getFilePatch(path: string, file: string, staged: boolean): Promise<string> {
  return invoke<string>("get_file_patch", { path, file, staged });
}

export function applyFilePatch(
  path: string,
  file: string,
  patch: string,
  action: PatchApplyAction,
): Promise<GitStatus> {
  return invoke<GitStatus>("apply_file_patch", { path, file, patch, action });
}

export function commit(path: string, message: string): Promise<GitStatus> {
  return invoke<GitStatus>("commit", { path, message });
}

export function amendCommit(path: string, message?: string): Promise<GitStatus> {
  return invoke<GitStatus>("amend_commit", { path, message: message?.trim() ? message.trim() : null });
}

export function lastCommitMessage(path: string): Promise<string | null> {
  return invoke<string | null>("last_commit_message", { path });
}

export function undoLastCommit(path: string): Promise<GitStatus> {
  return invoke<GitStatus>("undo_last_commit", { path });
}

export function stashPush(
  path: string,
  message?: string,
  includeUntracked = true,
  paths?: string[],
): Promise<GitStatus> {
  return invoke<GitStatus>("stash_push", {
    path,
    message: message?.trim() ? message.trim() : null,
    includeUntracked,
    paths: paths?.length ? paths : null,
  });
}

export function stashApply(path: string, selector: string): Promise<GitStatus> {
  return invoke<GitStatus>("stash_apply", { path, selector });
}

export function stashPop(path: string, selector: string): Promise<GitStatus> {
  return invoke<GitStatus>("stash_pop", { path, selector });
}

export function stashDrop(path: string, selector: string): Promise<GitStatus> {
  return invoke<GitStatus>("stash_drop", { path, selector });
}

export interface CommitAiGenerateOptions {
  notes?: string;
  provider: CommitAiProvider;
  model?: string;
  apiKey?: string;
  baseUrl?: string;
}

export function commitAiGenerate(path: string, options: CommitAiGenerateOptions): Promise<string> {
  return invoke<string>("commit_ai_generate", {
    path,
    notes: options.notes,
    provider: options.provider,
    model: options.model,
    apiKey: options.apiKey,
    baseUrl: options.baseUrl,
  });
}

export function commitAiReview(path: string, options: CommitAiGenerateOptions): Promise<AiReviewResult> {
  return invoke<AiReviewResult>("commit_ai_review", {
    path,
    provider: options.provider,
    model: options.model,
    apiKey: options.apiKey,
    baseUrl: options.baseUrl,
  });
}

export function commitAiSplit(path: string, options: CommitAiGenerateOptions): Promise<AiCommitPlan> {
  return invoke<AiCommitPlan>("commit_ai_split", {
    path,
    provider: options.provider,
    model: options.model,
    apiKey: options.apiKey,
    baseUrl: options.baseUrl,
  });
}

export function pull(
  path: string,
  username?: string,
  password?: string,
  strategy: PullStrategy = "merge",
  remote?: string,
  branch?: string,
  allowUnrelatedHistories = false,
): Promise<GitStatus> {
  return invoke<GitStatus>("pull", {
    path,
    username: username ?? null,
    password: password ?? null,
    strategy,
    remote: remote || null,
    branch: branch || null,
    allowUnrelatedHistories,
  });
}

export function fetchRemote(path: string, username?: string, password?: string, prune = false, remote?: string): Promise<GitStatus> {
  return invoke<GitStatus>("fetch", { path, username: username ?? null, password: password ?? null, prune, remote: remote || null });
}

export function push(path: string, username?: string, password?: string, forceWithLease = false, remote?: string): Promise<GitStatus> {
  return invoke<GitStatus>("push", { path, username: username ?? null, password: password ?? null, forceWithLease, remote: remote || null });
}

export function getRemoteUrl(path: string, remote?: string, push = false): Promise<string | null> {
  return invoke<string | null>("get_remote_url", { path, remote: remote || null, push });
}

export function credLoad(key: string): Promise<StoredCredential | null> {
  return invoke<StoredCredential | null>("cred_load", { key });
}

export function credSave(key: string, username: string, password: string, mode: "credentials" | "token" = "credentials"): Promise<void> {
  return invoke<void>("cred_save", { key, username, password, mode });
}

export function credDelete(key: string): Promise<void> {
  return invoke<void>("cred_delete", { key });
}

export function listCommits(path: string, limit = 100, skip = 0): Promise<GitCommit[]> {
  return invoke<GitCommit[]>("list_commits", { path, limit, skip });
}

export function getCommitNote(path: string, commit: string): Promise<string | null> {
  return invoke<string | null>("get_commit_note", { path, commit });
}

export function setCommitNote(path: string, commit: string, note: string): Promise<void> {
  return invoke<void>("set_commit_note", { path, commit, note });
}

export function deleteCommitNote(path: string, commit: string): Promise<void> {
  return invoke<void>("delete_commit_note", { path, commit });
}

export function fetchCommitNotes(path: string, remote: string, username?: string, password?: string): Promise<void> {
  return invoke<void>("fetch_commit_notes", {
    path,
    remote,
    username: username ?? null,
    password: password ?? null,
  });
}

export function pushCommitNotes(path: string, remote: string, username?: string, password?: string): Promise<void> {
  return invoke<void>("push_commit_notes", {
    path,
    remote,
    username: username ?? null,
    password: password ?? null,
  });
}

export function restoreToCommit(path: string, commit: string): Promise<GitStatus> {
  return invoke<GitStatus>("restore_to_commit", { path, commit });
}

export function restoreFileFromCommit(
  path: string,
  commit: string,
  file: string,
): Promise<GitStatus> {
  return invoke<GitStatus>("restore_file_from_commit", { path, commit, file });
}

export function mergeBranch(path: string, branch: string, strategy: MergeStrategy = "default"): Promise<GitStatus> {
  return invoke<GitStatus>("merge_branch", { path, branch, strategy });
}
export function mergeContinue(path: string): Promise<GitStatus> { return invoke("merge_continue", { path }); }
export function mergeAbort(path: string): Promise<GitStatus> { return invoke("merge_abort", { path }); }
export function revertCommit(path: string, commit: string): Promise<GitStatus> { return invoke("revert_commit", { path, commit }); }

export function rebaseBranch(path: string, branch: string): Promise<GitStatus> {
  return invoke<GitStatus>("rebase_branch", { path, branch });
}

export function rebaseContinue(path: string): Promise<GitStatus> {
  return invoke<GitStatus>("rebase_continue", { path });
}

export function rebaseAbort(path: string): Promise<GitStatus> {
  return invoke<GitStatus>("rebase_abort", { path });
}

export function listInteractiveRebaseCommits(path: string, base: string): Promise<RebaseCommit[]> {
  return invoke<RebaseCommit[]>("list_interactive_rebase_commits", { path, base });
}

export function startInteractiveRebase(
  path: string,
  base: string,
  plan: RebasePlanItem[],
): Promise<GitStatus> {
  return invoke<GitStatus>("start_interactive_rebase", { path, base, plan });
}

export function listReflog(path: string, limit = 250): Promise<ReflogEntry[]> {
  return invoke<ReflogEntry[]>("list_reflog", { path, limit });
}

export function restoreReflogEntry(path: string, commit: string, branch: string): Promise<GitStatus> {
  return invoke<GitStatus>("restore_reflog_entry", { path, commit, branch });
}

export function listRepositoryFiles(path: string): Promise<GitRepositoryFile[]> {
  return invoke<GitRepositoryFile[]>("list_repository_files", { path });
}

export function listFileHistory(
  path: string,
  file: string,
  limit = 100,
  requestId?: string,
): Promise<GitCommit[]> {
  return invoke<GitCommit[]>("list_file_history", { path, file, limit, requestId: requestId ?? null });
}

export function cancelFileHistory(requestId: string): Promise<void> {
  return invoke<void>("cancel_file_history", { requestId });
}

export function getFileBlame(path: string, file: string): Promise<GitBlameResult> {
  return invoke<GitBlameResult>("get_file_blame", { path, file });
}

export function compareCommits(
  path: string,
  from: string,
  to: string,
): Promise<GitCommitComparison> {
  return invoke<GitCommitComparison>("compare_commits", { path, from, to });
}

export function diffFileAgainstWorkingTree(
  path: string,
  commit: string,
  file: string,
): Promise<GitCommitComparison> {
  return invoke<GitCommitComparison>("diff_file_against_working_tree", { path, commit, file });
}

export function compareFileToHead(
  path: string,
  commit: string,
  file: string,
): Promise<GitCommitComparison> {
  return invoke<GitCommitComparison>("compare_file_to_head", { path, commit, file });
}

export function compareFileToParent(
  path: string,
  commit: string,
  file: string,
  oldFile: string | null = null,
): Promise<GitCommitComparison> {
  return invoke<GitCommitComparison>("compare_file_to_parent", { path, commit, file, oldFile });
}

export function searchCodeIntroductions(
  path: string,
  query: string,
  caseSensitive = false,
  limit = 250,
  searchId?: string,
): Promise<GitSearchHit[]> {
  return invoke<GitSearchHit[]>("search_code_introductions", {
    path,
    query,
    caseSensitive,
    limit,
    searchId: searchId ?? null,
  });
}

export function cancelCodeSearch(searchId: string): Promise<void> {
  return invoke<void>("cancel_code_search", { searchId });
}

export function readConflict(path: string, file: string): Promise<ConflictFile> {
  return invoke<ConflictFile>("read_conflict", { path, file });
}

export function resolveConflict(
  path: string,
  file: string,
  content: string,
): Promise<GitStatus> {
  return invoke<GitStatus>("resolve_conflict", { path, file, content });
}

export function resolveConflictSide(
  path: string,
  file: string,
  side: "ours" | "theirs",
): Promise<GitStatus> {
  return invoke<GitStatus>("resolve_conflict_side", { path, file, side });
}
