
import * as gitHttp from './gitApiHttp';
import { opencodeClient } from './opencode/client';
import { renderMagicPrompt } from './magicPrompts';
import { materializeOpenDraftSession, useSessionUIStore } from '@/sync/session-ui-store';
import { useSelectionStore } from '@/sync/selection-store';
import { useConfigStore } from '@/stores/useConfigStore';
import { getRegisteredRuntimeAPIs } from '@/contexts/runtimeAPIRegistry';

export type {
  GitStatus,
  GitDiffResponse,
  GetGitDiffOptions,
  GitBranchDetails,
  GitBranch,
  GitCommitResult,
  GitPushResult,
  GitPullResult,
  GitIdentityProfile,
  GitIdentityAuthType,
  GitIdentitySummary,
  GitLogEntry,
  GitLogResponse,
  GitWorktreeInfo,
  CreateGitWorktreePayload,
  GitWorktreeCreateResult,
  RemoveGitWorktreePayload,
  GitWorktreeValidationError,
  GitWorktreeValidationResult,
  GitDeleteBranchPayload,
  GitDeleteRemoteBranchPayload,
  GitRemoveRemotePayload,
  DiscoveredGitCredential,
  GitRemote,
  GitMergeResult,
  GitRebaseResult,
  MergeConflictDetails,
  CommitFileDiffResponse,
} from './api/types';

const getRuntimeGit = () => {
  return getRegisteredRuntimeAPIs()?.git ?? null;
};

const requestChatForceScrollBottom = (sessionId: string) => {
  if (typeof window === 'undefined') return;
  window.dispatchEvent(new CustomEvent('openchamber:chat-force-scroll-bottom', {
    detail: { sessionId },
  }));
};

const extractJsonObject = (value: string): Record<string, unknown> | null => {
  const text = value.trim();
  const fenced = text.match(/```(?:json)?\s*([\s\S]*?)```/i);
  const candidate = (fenced?.[1] ?? text).trim();
  const starts = [candidate.indexOf('{')].filter((index) => index >= 0);

  for (const start of starts) {
    for (let end = candidate.length; end > start; end -= 1) {
      if (candidate[end - 1] !== '}') continue;
      try {
        const parsed = JSON.parse(candidate.slice(start, end)) as unknown;
        if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
          return parsed as Record<string, unknown>;
        }
      } catch {
        // Keep scanning; models sometimes wrap JSON with prose or fences.
      }
    }
  }

  return null;
};

const extractAssistantText = (response: unknown): string => {
  const data = (response as { data?: { parts?: Array<unknown> } } | null)?.data;
  const parts = Array.isArray(data?.parts) ? data.parts : [];
  return parts
    .map((part) => {
      const item = part as { type?: unknown; text?: unknown; content?: unknown; value?: unknown };
      if (item.type !== 'text') return '';
      if (typeof item.text === 'string') return item.text;
      if (typeof item.content === 'string') return item.content;
      if (typeof item.value === 'string') return item.value;
      return '';
    })
    .filter((text) => text.trim().length > 0)
    .join('\n')
    .trim();
};

export async function checkIsGitRepository(directory: string): Promise<boolean> {
  const runtime = getRuntimeGit();
  if (runtime) return runtime.checkIsGitRepository(directory);
  return gitHttp.checkIsGitRepository(directory);
}

export async function getGitStatus(directory: string, options?: { mode?: 'light' }): Promise<import('./api/types').GitStatus> {
  const runtime = getRuntimeGit();
  if (runtime) return runtime.getGitStatus(directory, options);
  return gitHttp.getGitStatus(directory, options);
}

export async function resolveGitPrimaryRoot(directory: string): Promise<string> {
  const result = await gitHttp.resolveGitPrimaryRoot(directory);
  return result.root;
}

export async function resolveGitTopLevel(directory: string): Promise<string> {
  const result = await gitHttp.resolveGitTopLevel(directory);
  return result.root;
}

export async function getGitCommitSummaries(
  directory: string,
  shas: string[]
): Promise<Array<{ sha: string; short: string; subject: string }>> {
  const result = await gitHttp.getGitCommitSummaries(directory, shas);
  return result.commits;
}

export async function getGitDiff(directory: string, options: import('./api/types').GetGitDiffOptions): Promise<import('./api/types').GitDiffResponse> {
  const runtime = getRuntimeGit();
  if (runtime) return runtime.getGitDiff(directory, options);
  return gitHttp.getGitDiff(directory, options);
}

export async function getGitFileDiff(
  directory: string,
  options: import('./api/types').GetGitFileDiffOptions
): Promise<import('./api/types').GitFileDiffResponse> {
  const runtime = getRuntimeGit();
  if (runtime) return runtime.getGitFileDiff(directory, options);
  return gitHttp.getGitFileDiff(directory, options);
}

export async function revertGitFile(
  directory: string,
  filePath: string,
  options?: { scope?: 'all' | 'working' }
): Promise<void> {
  const runtime = getRuntimeGit();
  if (runtime) return runtime.revertGitFile(directory, filePath, options);
  return gitHttp.revertGitFile(directory, filePath, options);
}

export async function stageGitFile(directory: string, filePath: string): Promise<void> {
  const runtime = getRuntimeGit();
  if (runtime?.stageGitFile) return runtime.stageGitFile(directory, filePath);
  return gitHttp.stageGitFile(directory, filePath);
}

export async function stageGitFiles(directory: string, filePaths: string[]): Promise<void> {
  const runtime = getRuntimeGit();
  if (runtime?.stageGitFiles) return runtime.stageGitFiles(directory, filePaths);
  return gitHttp.stageGitFiles(directory, filePaths);
}

export async function unstageGitFile(directory: string, filePath: string): Promise<void> {
  const runtime = getRuntimeGit();
  if (runtime?.unstageGitFile) return runtime.unstageGitFile(directory, filePath);
  return gitHttp.unstageGitFile(directory, filePath);
}

export async function unstageGitFiles(directory: string, filePaths: string[]): Promise<void> {
  const runtime = getRuntimeGit();
  if (runtime?.unstageGitFiles) return runtime.unstageGitFiles(directory, filePaths);
  return gitHttp.unstageGitFiles(directory, filePaths);
}

export async function stageGitHunk(directory: string, filePath: string, patch: string): Promise<void> {
  const runtime = getRuntimeGit();
  if (runtime?.stageGitHunk) return runtime.stageGitHunk(directory, filePath, patch);
  return gitHttp.stageGitHunk(directory, filePath, patch);
}

export async function unstageGitHunk(directory: string, filePath: string, patch: string): Promise<void> {
  const runtime = getRuntimeGit();
  if (runtime?.unstageGitHunk) return runtime.unstageGitHunk(directory, filePath, patch);
  return gitHttp.unstageGitHunk(directory, filePath, patch);
}

export async function revertGitHunk(directory: string, filePath: string, patch: string): Promise<void> {
  const runtime = getRuntimeGit();
  if (runtime?.revertGitHunk) return runtime.revertGitHunk(directory, filePath, patch);
  return gitHttp.revertGitHunk(directory, filePath, patch);
}

export async function isLinkedWorktree(directory: string): Promise<boolean> {
  const runtime = getRuntimeGit();
  if (runtime) return runtime.isLinkedWorktree(directory);
  return gitHttp.isLinkedWorktree(directory);
}

export async function getGitBranches(directory: string): Promise<import('./api/types').GitBranch> {
  const runtime = getRuntimeGit();
  if (runtime) return runtime.getGitBranches(directory);
  return gitHttp.getGitBranches(directory);
}

export async function deleteGitBranch(directory: string, payload: import('./api/types').GitDeleteBranchPayload): Promise<{ success: boolean }> {
  const runtime = getRuntimeGit();
  if (runtime) return runtime.deleteGitBranch(directory, payload);
  return gitHttp.deleteGitBranch(directory, payload);
}

export async function deleteRemoteBranch(directory: string, payload: import('./api/types').GitDeleteRemoteBranchPayload): Promise<{ success: boolean }> {
  const runtime = getRuntimeGit();
  if (runtime) return runtime.deleteRemoteBranch(directory, payload);
  return gitHttp.deleteRemoteBranch(directory, payload);
}

export async function generateCommitMessage(
  directory: string,
  files: string[],
  options?: { zenModel?: string; providerId?: string; modelId?: string }
): Promise<{ message: import('./api/types').GeneratedCommitMessage }> {
  const startedAt = Date.now();
  void options;
  const generationSession = await resolveGenerationSessionContext();

  console.info('[git-generation][browser] request', {
    transport: 'session',
    kind: 'commit',
    directory,
    selectedFiles: files.length,
    sessionId: generationSession.sessionId,
    providerId: generationSession.providerID,
    modelId: generationSession.modelID,
    agent: generationSession.agent,
  });

  const visiblePrompt = await renderMagicPrompt('git.commit.generate.visible');
  const hiddenPrompt = await renderMagicPrompt('git.commit.generate.instructions', {
    selected_files: files.map((file) => `- ${file}`).join('\n'),
  });

  try {
    const structured = await runStructuredGenerationInActiveSession({
      directory,
      visiblePrompt,
      hiddenPrompt,
      generationSession,
      kind: 'commit',
    });

    const subject = typeof structured.subject === 'string' ? structured.subject.trim() : '';
    const highlights = Array.isArray(structured.highlights)
      ? structured.highlights.filter((item) => typeof item === 'string').map((item) => item.trim()).filter(Boolean).slice(0, 3)
      : [];

    if (!subject) {
      throw new Error('Structured output missing subject');
    }

    const result = { message: { subject, highlights } };
    console.info('[git-generation][browser] success', {
      transport: 'session',
      kind: 'commit',
      elapsedMs: Date.now() - startedAt,
      subjectLength: result.message.subject.length,
      highlightsCount: result.message.highlights.length,
    });
    return result;
  } catch (error) {
    console.error('[git-generation][browser] failed', {
      transport: 'session',
      kind: 'commit',
      elapsedMs: Date.now() - startedAt,
      message: error instanceof Error ? error.message : String(error),
      error,
    });
    throw error;
  }
}

export async function generatePullRequestDescription(
  directory: string,
  payload: { base: string; head: string; context?: string; zenModel?: string; providerId?: string; modelId?: string }
): Promise<import('./api/types').GeneratedPullRequestDescription> {
  const startedAt = Date.now();
  const generationSession = await resolveGenerationSessionContext();

  const commitLog = await getGitLog(directory, {
    from: payload.base,
    to: payload.head,
    maxCount: 50,
  });
  const commits = (Array.isArray(commitLog?.all) ? commitLog.all : [])
    .filter((entry) => typeof entry?.hash === 'string' && entry.hash.length > 0)
    .map((entry) => ({
      hash: entry.hash,
      subject: typeof entry.message === 'string' ? entry.message.trim() : '',
    }));

  if (commits.length === 0) {
    throw new Error(`No commits found in range ${payload.base}...${payload.head}`);
  }

  const filesSet = new Set<string>();
  await Promise.all(commits.map(async (commit) => {
    try {
      const response = await getCommitFiles(directory, commit.hash);
      const files = Array.isArray(response?.files) ? response.files : [];
      for (const file of files) {
        if (typeof file?.path === 'string' && file.path.trim().length > 0) {
          filesSet.add(file.path.trim());
        }
      }
    } catch (error) {
      console.warn('[git-generation][browser] failed to collect commit files', {
        hash: commit.hash,
        error: error instanceof Error ? error.message : String(error),
      });
    }
  }));
  const changedFiles = Array.from(filesSet).sort().slice(0, 300);

  console.info('[git-generation][browser] request', {
    transport: 'session',
    kind: 'pr',
    directory,
    sessionId: generationSession.sessionId,
    providerId: generationSession.providerID,
    modelId: generationSession.modelID,
    agent: generationSession.agent,
    base: payload.base,
    head: payload.head,
    commits: commits.length,
    changedFiles: changedFiles.length,
  });

  const visiblePrompt = await renderMagicPrompt('git.pr.generate.visible');
  const hiddenPrompt = await renderMagicPrompt('git.pr.generate.instructions', {
    base_branch: payload.base,
    head_branch: payload.head,
    commits: commits.map((commit) => `- ${commit.hash.slice(0, 7)} ${commit.subject || '(no subject)'}`).join('\n'),
    changed_files: changedFiles.length > 0 ? changedFiles.map((file) => `- ${file}`).join('\n') : '- none detected',
    additional_context_block: payload.context?.trim() ? `\nAdditional context:\n${payload.context.trim()}` : '',
  });

  try {
    const structured = await runStructuredGenerationInActiveSession({
      directory,
      visiblePrompt,
      hiddenPrompt,
      generationSession,
      kind: 'pr',
    });

    const result = {
      title: typeof structured.title === 'string' ? structured.title.trim() : '',
      body: typeof structured.body === 'string' ? structured.body.trim() : '',
    };
    console.info('[git-generation][browser] success', {
      transport: 'session',
      kind: 'pr',
      elapsedMs: Date.now() - startedAt,
      titleLength: result.title.length,
      bodyLength: result.body.length,
    });
    return result;
  } catch (error) {
    console.error('[git-generation][browser] failed', {
      transport: 'session',
      kind: 'pr',
      elapsedMs: Date.now() - startedAt,
      message: error instanceof Error ? error.message : String(error),
      error,
    });
    throw error;
  }
}

type SessionGenerationContext = {
  sessionId: string;
  providerID: string;
  modelID: string;
  agent?: string;
  variant?: string;
};

const GENERATION_CONFIG_ERROR = 'No default provider or model configured. Please select a provider and model in settings first.';

async function resolveGenerationSessionContext(): Promise<SessionGenerationContext> {
  const activeSession = resolveSessionGenerationContext();
  if (activeSession) {
    return activeSession;
  }

  const draft = useSessionUIStore.getState().newSessionDraft;
  if (!draft?.open) {
    throw new Error('Select existing session for generation');
  }

  const config = useConfigStore.getState();
  if (!config.currentProviderId || !config.currentModelId) {
    throw new Error(GENERATION_CONFIG_ERROR);
  }

  const createdDraftSession = await materializeOpenDraftSession({
    providerID: config.currentProviderId,
    modelID: config.currentModelId,
    agent: config.currentAgentName || undefined,
    variant: config.currentVariant || undefined,
  });

  if (!createdDraftSession) {
    const retry = resolveSessionGenerationContext();
    if (retry) {
      return retry;
    }
    throw new Error('Failed to create session for generation');
  }

  return {
    sessionId: createdDraftSession.sessionId,
    providerID: config.currentProviderId,
    modelID: config.currentModelId,
    agent: createdDraftSession.agent,
    variant: config.currentVariant || undefined,
  };
}

const resolveSessionGenerationContext = (): SessionGenerationContext | null => {
  const sessionId = useSessionUIStore.getState().currentSessionId;
  if (!sessionId) {
    return null;
  }

  const selection = useSelectionStore.getState();
  const config = useConfigStore.getState();
  const lastChoice = useSessionUIStore.getState().getLastUserChoice(sessionId);

  const agent = selection.getSessionAgentSelection(sessionId) || lastChoice?.agent || config.currentAgentName || undefined;
  const sessionModel = selection.getSessionModelSelection(sessionId);
  const agentModel = agent ? selection.getAgentModelForSession(sessionId, agent) : null;
  const lastChoiceModel = lastChoice?.providerID && lastChoice.modelID
    ? { providerId: lastChoice.providerID, modelId: lastChoice.modelID }
    : null;
  const selectedModel = agentModel || sessionModel || lastChoiceModel || (config.currentProviderId && config.currentModelId
    ? { providerId: config.currentProviderId, modelId: config.currentModelId }
    : null);

  if (!selectedModel?.providerId || !selectedModel?.modelId) {
    return null;
  }

  const selectionVariant = agent
    ? selection.getAgentModelVariantForSession(sessionId, agent, selectedModel.providerId, selectedModel.modelId)
    : undefined;
  const lastChoiceVariant = lastChoiceModel
    && lastChoiceModel.providerId === selectedModel.providerId
    && lastChoiceModel.modelId === selectedModel.modelId
      ? lastChoice?.variant
      : undefined;
  const configVariant = config.currentProviderId === selectedModel.providerId && config.currentModelId === selectedModel.modelId
    ? config.currentVariant
    : undefined;
  const variant = selectionVariant || lastChoiceVariant || configVariant || undefined;

  return {
    sessionId,
    providerID: selectedModel.providerId,
    modelID: selectedModel.modelId,
    agent,
    variant,
  };
};

const runStructuredGenerationInActiveSession = async ({
  directory,
  visiblePrompt,
  hiddenPrompt,
  generationSession,
  kind,
}: {
  directory: string;
  visiblePrompt: string;
  hiddenPrompt?: string;
  generationSession: SessionGenerationContext;
  kind: 'commit' | 'pr';
}): Promise<Record<string, unknown>> => {
  const requestStartedAt = Date.now();
  console.info('[git-generation][browser] runStructuredGenerationInActiveSession start', {
    kind,
    directory,
    sessionId: generationSession.sessionId,
    providerID: generationSession.providerID,
    modelID: generationSession.modelID,
    agent: generationSession.agent,
    variant: generationSession.variant,
  });
  const trimmedDirectory = typeof directory === 'string' ? directory.trim() : '';
  const visiblePromptText = typeof visiblePrompt === 'string' ? visiblePrompt.trim() : '';
  const hiddenPromptText = typeof hiddenPrompt === 'string' ? hiddenPrompt.trim() : '';
  const promptParts: Array<{ type: 'text'; text: string; synthetic?: boolean }> = [];
  if (visiblePromptText) {
    promptParts.push({
      type: 'text',
      text: hiddenPromptText ? `${visiblePromptText}\n\n` : visiblePromptText,
      synthetic: false,
    });
  }
  if (hiddenPromptText) {
    promptParts.push({ type: 'text', text: hiddenPromptText, synthetic: true });
  }
  if (promptParts.length === 0) {
    throw new Error('Generation prompts are empty');
  }

  requestChatForceScrollBottom(generationSession.sessionId);

  const response = await opencodeClient.withDirectory(directory, async () => {
    return opencodeClient.getApiClient().session.prompt({
      sessionID: generationSession.sessionId,
      ...(trimmedDirectory.length > 0 ? { directory: trimmedDirectory } : {}),
      model: {
        providerID: generationSession.providerID,
        modelID: generationSession.modelID,
      },
      ...(generationSession.agent ? { agent: generationSession.agent } : {}),
      ...(generationSession.variant ? { variant: generationSession.variant } : {}),
      parts: promptParts,
    });
  });

  const responseError = response?.error as { message?: string } | undefined;
  if (!response?.data) {
    throw new Error(responseError?.message || `Failed to generate ${kind} output`);
  }

  const info = response.data.info as { finish?: string; error?: unknown };
  const assistantText = extractAssistantText(response);
  const parsedOutput = extractJsonObject(assistantText);
  if (!parsedOutput) {
    console.error('[git-generation][browser] invalid JSON output', {
      kind,
      sessionId: generationSession.sessionId,
      elapsedMs: Date.now() - requestStartedAt,
      finish: info?.finish,
      assistantText,
      messageInfo: response.data.info,
      messageParts: response.data.parts,
    });
    throw new Error('No JSON output returned by session');
  }

  return parsedOutput;
};

export async function listGitWorktrees(directory: string): Promise<import('./api/types').GitWorktreeInfo[]> {
  const runtime = getRuntimeGit();
  if (runtime?.worktree?.list) {
    return runtime.worktree.list(directory);
  }
  if (runtime) return runtime.listGitWorktrees(directory);
  return gitHttp.listGitWorktrees(directory);
}

export async function validateGitWorktree(
  directory: string,
  payload: import('./api/types').CreateGitWorktreePayload
): Promise<import('./api/types').GitWorktreeValidationResult> {
  const runtime = getRuntimeGit();
  if (runtime?.worktree?.validate) {
    return runtime.worktree.validate(directory, payload);
  }
  if (runtime?.validateGitWorktree) {
    return runtime.validateGitWorktree(directory, payload);
  }
  return gitHttp.validateGitWorktree(directory, payload);
}

export async function getGitWorktreeBootstrapStatus(
  directory: string,
): Promise<import('./api/types').GitWorktreeBootstrapStatus> {
  const runtime = getRuntimeGit();
  if (runtime?.worktree?.bootstrapStatus) {
    return runtime.worktree.bootstrapStatus(directory);
  }
  if (runtime?.getGitWorktreeBootstrapStatus) {
    return runtime.getGitWorktreeBootstrapStatus(directory);
  }
  return gitHttp.getGitWorktreeBootstrapStatus(directory);
}

export async function previewGitWorktree(
  directory: string,
  payload: import('./api/types').CreateGitWorktreePayload
): Promise<import('./api/types').GitWorktreeCreateResult> {
  const runtime = getRuntimeGit();
  if (runtime?.worktree?.preview) {
    return runtime.worktree.preview(directory, payload);
  }
  if (runtime?.previewGitWorktree) {
    return runtime.previewGitWorktree(directory, payload);
  }
  return gitHttp.previewGitWorktree(directory, payload);
}

export async function createGitWorktree(
  directory: string,
  payload: import('./api/types').CreateGitWorktreePayload
): Promise<import('./api/types').GitWorktreeCreateResult> {
  const runtime = getRuntimeGit();
  if (runtime?.worktree?.create) {
    return runtime.worktree.create(directory, payload);
  }
  if (runtime?.createGitWorktree) {
    return runtime.createGitWorktree(directory, payload);
  }
  return gitHttp.createGitWorktree(directory, payload);
}

export async function deleteGitWorktree(
  directory: string,
  payload: import('./api/types').RemoveGitWorktreePayload
): Promise<{ success: boolean }> {
  const runtime = getRuntimeGit();
  if (runtime?.worktree?.remove) {
    return runtime.worktree.remove(directory, payload);
  }
  if (runtime?.deleteGitWorktree) {
    return runtime.deleteGitWorktree(directory, payload);
  }
  return gitHttp.deleteGitWorktree(directory, payload);
}

export const git = {
  worktree: {
    list: listGitWorktrees,
    validate: validateGitWorktree,
    create: createGitWorktree,
    remove: deleteGitWorktree,
  },
};

export async function createGitCommit(
  directory: string,
  message: string,
  options: import('./api/types').CreateGitCommitOptions = {}
): Promise<import('./api/types').GitCommitResult> {
  const runtime = getRuntimeGit();
  if (runtime) return runtime.createGitCommit(directory, message, options);
  return gitHttp.createGitCommit(directory, message, options);
}

export async function gitPush(
  directory: string,
  options: { remote?: string; branch?: string; options?: string[] | Record<string, unknown> } = {}
): Promise<import('./api/types').GitPushResult> {
  const runtime = getRuntimeGit();
  if (runtime) return runtime.gitPush(directory, options);
  return gitHttp.gitPush(directory, options);
}

export async function gitPull(
  directory: string,
  options: import('./api/types').GitPullOptions = {}
): Promise<import('./api/types').GitPullResult> {
  const runtime = getRuntimeGit();
  if (runtime) return runtime.gitPull(directory, options);
  return gitHttp.gitPull(directory, options);
}

export async function gitFetch(
  directory: string,
  options: { remote?: string; branch?: string } = {}
): Promise<{ success: boolean }> {
  const runtime = getRuntimeGit();
  if (runtime) return runtime.gitFetch(directory, options);
  return gitHttp.gitFetch(directory, options);
}

export async function listGitStashes(directory: string): Promise<{ stashes: import('./api/types').GitStashEntry[] }> {
  const runtime = getRuntimeGit();
  if (runtime) return runtime.listGitStashes(directory);
  return gitHttp.listGitStashes(directory);
}

export async function countGitStashFiles(directory: string, refs: string[]): Promise<{ counts: Record<string, number> }> {
  const runtime = getRuntimeGit();
  if (runtime) return runtime.countGitStashFiles(directory, refs);
  return gitHttp.countGitStashFiles(directory, refs);
}

export async function stashGitChanges(directory: string, options: { message?: string } = {}): Promise<{ success: boolean; created: boolean; message: string; output: string }> {
  const runtime = getRuntimeGit();
  if (runtime) return runtime.stashGitChanges(directory, options);
  return gitHttp.stashGitChanges(directory, options);
}

export async function applyGitStash(directory: string, options: { ref: string }): Promise<{ success: boolean; ref: string }> {
  const runtime = getRuntimeGit();
  if (runtime) return runtime.applyGitStash(directory, options);
  return gitHttp.applyGitStash(directory, options);
}

export async function popGitStash(directory: string, options: { ref: string }): Promise<{ success: boolean; ref: string }> {
  const runtime = getRuntimeGit();
  if (runtime) return runtime.popGitStash(directory, options);
  return gitHttp.popGitStash(directory, options);
}

export async function dropGitStash(directory: string, options: { ref: string }): Promise<{ success: boolean; ref: string }> {
  const runtime = getRuntimeGit();
  if (runtime) return runtime.dropGitStash(directory, options);
  return gitHttp.dropGitStash(directory, options);
}

export async function checkoutBranch(directory: string, branch: string): Promise<{ success: boolean; branch: string }> {
  const runtime = getRuntimeGit();
  if (runtime) return runtime.checkoutBranch(directory, branch);
  return gitHttp.checkoutBranch(directory, branch);
}

export async function createBranch(
  directory: string,
  name: string,
  startPoint?: string
): Promise<{ success: boolean; branch: string }> {
  const runtime = getRuntimeGit();
  if (runtime) return runtime.createBranch(directory, name, startPoint);
  return gitHttp.createBranch(directory, name, startPoint);
}

export async function renameBranch(
  directory: string,
  oldName: string,
  newName: string
): Promise<{ success: boolean; branch: string }> {
  const runtime = getRuntimeGit();
  if (runtime) return runtime.renameBranch(directory, oldName, newName);
  return gitHttp.renameBranch(directory, oldName, newName);
}

export async function getGitLog(
  directory: string,
  options: import('./api/types').GitLogOptions = {}
): Promise<import('./api/types').GitLogResponse> {
  const runtime = getRuntimeGit();
  if (runtime) return runtime.getGitLog(directory, options);
  return gitHttp.getGitLog(directory, options);
}

export async function getCommitFiles(
  directory: string,
  hash: string
): Promise<import('./api/types').GitCommitFilesResponse> {
  const runtime = getRuntimeGit();
  if (runtime) return runtime.getCommitFiles(directory, hash);
  return gitHttp.getCommitFiles(directory, hash);
}

export async function getCommitFileDiff(
  directory: string,
  hash: string,
  filePath: string,
  isBinary: boolean
): Promise<import('./api/types').CommitFileDiffResponse> {
  const runtime = getRuntimeGit();
  if (runtime?.getCommitFileDiff) return runtime.getCommitFileDiff(directory, hash, filePath, isBinary);
  return gitHttp.getCommitFileDiff(directory, hash, filePath, isBinary);
}

export async function getGitIdentities(): Promise<import('./api/types').GitIdentityProfile[]> {
  const runtime = getRuntimeGit();
  if (runtime) return runtime.getGitIdentities();
  return gitHttp.getGitIdentities();
}

export async function createGitIdentity(profile: import('./api/types').GitIdentityProfile): Promise<import('./api/types').GitIdentityProfile> {
  const runtime = getRuntimeGit();
  if (runtime) return runtime.createGitIdentity(profile);
  return gitHttp.createGitIdentity(profile);
}

export async function updateGitIdentity(id: string, updates: import('./api/types').GitIdentityProfile): Promise<import('./api/types').GitIdentityProfile> {
  const runtime = getRuntimeGit();
  if (runtime) return runtime.updateGitIdentity(id, updates);
  return gitHttp.updateGitIdentity(id, updates);
}

export async function deleteGitIdentity(id: string): Promise<void> {
  const runtime = getRuntimeGit();
  if (runtime) return runtime.deleteGitIdentity(id);
  return gitHttp.deleteGitIdentity(id);
}

export async function getCurrentGitIdentity(directory: string): Promise<import('./api/types').GitIdentitySummary | null> {
  const runtime = getRuntimeGit();
  if (runtime) return runtime.getCurrentGitIdentity(directory);
  return gitHttp.getCurrentGitIdentity(directory);
}

export async function hasLocalIdentity(directory: string): Promise<boolean> {
  const runtime = getRuntimeGit();
  if (runtime?.hasLocalIdentity) return runtime.hasLocalIdentity(directory);
  return gitHttp.hasLocalIdentity(directory);
}

export async function setGitIdentity(
  directory: string,
  profileId: string
): Promise<{ success: boolean; profile: import('./api/types').GitIdentityProfile }> {
  const runtime = getRuntimeGit();
  if (runtime) return runtime.setGitIdentity(directory, profileId);
  return gitHttp.setGitIdentity(directory, profileId);
}

export async function discoverGitCredentials(): Promise<import('./api/types').DiscoveredGitCredential[]> {
  const runtime = getRuntimeGit();
  if (runtime?.discoverGitCredentials) return runtime.discoverGitCredentials();
  return gitHttp.discoverGitCredentials();
}

export async function getGlobalGitIdentity(): Promise<import('./api/types').GitIdentitySummary | null> {
  const runtime = getRuntimeGit();
  if (runtime?.getGlobalGitIdentity) return runtime.getGlobalGitIdentity();
  return gitHttp.getGlobalGitIdentity();
}

export async function getRemoteUrl(directory: string, remote?: string): Promise<string | null> {
  const runtime = getRuntimeGit();
  if (runtime?.getRemoteUrl) return runtime.getRemoteUrl(directory, remote);
  return gitHttp.getRemoteUrl(directory, remote);
}

export async function getRemotes(directory: string): Promise<import('./api/types').GitRemote[]> {
  const runtime = getRuntimeGit();
  if (runtime) return runtime.getRemotes(directory);
  return gitHttp.getRemotes(directory);
}

export async function removeRemote(
  directory: string,
  payload: import('./api/types').GitRemoveRemotePayload
): Promise<{ success: boolean }> {
  const runtime = getRuntimeGit();
  if (runtime) return runtime.removeRemote(directory, payload);
  return gitHttp.removeRemote(directory, payload);
}

export async function rebase(
  directory: string,
  options: { onto: string }
): Promise<import('./api/types').GitRebaseResult> {
  const runtime = getRuntimeGit();
  if (runtime) return runtime.rebase(directory, options);
  return gitHttp.rebase(directory, options);
}

export async function abortRebase(directory: string): Promise<{ success: boolean }> {
  const runtime = getRuntimeGit();
  if (runtime) return runtime.abortRebase(directory);
  return gitHttp.abortRebase(directory);
}

export async function merge(
  directory: string,
  options: { branch: string }
): Promise<import('./api/types').GitMergeResult> {
  const runtime = getRuntimeGit();
  if (runtime) return runtime.merge(directory, options);
  return gitHttp.merge(directory, options);
}

export async function checkoutCommit(
  directory: string,
  hash: string
): Promise<import('./api/types').CheckoutCommitResponse> {
  const runtime = getRuntimeGit();
  if (runtime) return runtime.checkoutCommit(directory, hash);
  return gitHttp.checkoutCommit(directory, hash);
}

export async function cherryPick(
  directory: string,
  hash: string
): Promise<import('./api/types').CherryPickResponse> {
  const runtime = getRuntimeGit();
  if (runtime) return runtime.cherryPick(directory, hash);
  return gitHttp.cherryPick(directory, hash);
}

export async function revertCommit(
  directory: string,
  hash: string
): Promise<import('./api/types').RevertCommitResponse> {
  const runtime = getRuntimeGit();
  if (runtime) return runtime.revertCommit(directory, hash);
  return gitHttp.revertCommit(directory, hash);
}

export async function resetToCommit(
  directory: string,
  hash: string,
  mode: 'soft' | 'mixed' | 'hard',
  force?: boolean
): Promise<import('./api/types').ResetToCommitResponse> {
  const runtime = getRuntimeGit();
  if (runtime) return runtime.resetToCommit(directory, hash, mode, force);
  return gitHttp.resetToCommit(directory, hash, mode, force);
}

export async function abortMerge(directory: string): Promise<{ success: boolean }> {
  const runtime = getRuntimeGit();
  if (runtime) return runtime.abortMerge(directory);
  return gitHttp.abortMerge(directory);
}

export async function continueRebase(directory: string): Promise<{ success: boolean; conflict: boolean; conflictFiles?: string[] }> {
  const runtime = getRuntimeGit();
  if (runtime) return runtime.continueRebase(directory);
  return gitHttp.continueRebase(directory);
}

export async function continueMerge(directory: string): Promise<{ success: boolean; conflict: boolean; conflictFiles?: string[] }> {
  const runtime = getRuntimeGit();
  if (runtime) return runtime.continueMerge(directory);
  return gitHttp.continueMerge(directory);
}

export async function stash(
  directory: string,
  options?: { message?: string; includeUntracked?: boolean }
): Promise<{ success: boolean }> {
  const runtime = getRuntimeGit();
  if (runtime) return runtime.stash(directory, options);
  return gitHttp.stash(directory, options);
}

export async function stashPop(directory: string): Promise<{ success: boolean }> {
  const runtime = getRuntimeGit();
  if (runtime) return runtime.stashPop(directory);
  return gitHttp.stashPop(directory);
}

export async function getConflictDetails(directory: string): Promise<import('./api/types').MergeConflictDetails> {
  const runtime = getRuntimeGit();
  if (runtime?.getConflictDetails) return runtime.getConflictDetails(directory);
  return gitHttp.getConflictDetails(directory);
}

export async function validateWorktreeDirectory(
  directory: string,
  worktreeRoot: string
): Promise<{
  valid: boolean;
  insideWorktreeRoot: boolean;
  resolvedWorktreeRoot: string | null;
  resolvedCwd: string | null;
}> {
  const runtime = getRuntimeGit();
  if (runtime?.validateWorktreeDirectory) {
    return runtime.validateWorktreeDirectory(directory, worktreeRoot);
  }
  return gitHttp.validateWorktreeDirectory(directory, worktreeRoot);
}

export async function canonicalizeWorktreeState(
  directory: string
): Promise<{
  worktreeRoot: string | null;
  cwd: string | null;
  branch: string | null;
  headState: 'branch' | 'detached' | 'unborn';
  worktreeStatus: 'pending' | 'ready' | 'missing' | 'invalid' | 'not-a-repo';
  legacy: boolean;
  degraded: boolean;
  attentionReason?: 'merge' | 'rebase' | 'cherry-pick' | 'revert' | 'bisect' | null;
}> {
  const runtime = getRuntimeGit();
  if (runtime?.canonicalizeWorktreeState) {
    return runtime.canonicalizeWorktreeState(directory);
  }
  return gitHttp.canonicalizeWorktreeState(directory);
}
