/**
 * VS Code Git API implementation
 * Uses bridge messages to communicate with the extension host
 */

import { sendBridgeMessage } from './bridge';
import type {
  GitAPI,
  GitStatus,
  GitDiffResponse,
  GetGitDiffOptions,
  GitFileDiffResponse,
  GetGitFileDiffOptions,
  GitBranch,
  GitDeleteBranchPayload,
  GitDeleteRemoteBranchPayload,
  GitRemoveRemotePayload,
  GeneratedCommitMessage,
  GeneratedPullRequestDescription,
  GitWorktreeInfo,
  CreateGitWorktreePayload,
  GitWorktreeValidationResult,
  GitWorktreeCreateResult,
  RemoveGitWorktreePayload,
  GitCommitResult,
  CreateGitCommitOptions,
  GitPushResult,
  GitPullResult,
  GitLogResponse,
  GitLogOptions,
  GitCommitFilesResponse,
  CommitFileDiffResponse,
  GitIdentitySummary,
  GitIdentityProfile,
  GitRemote,
  GitRebaseResult,
  GitMergeResult,
  CheckoutCommitResponse,
  CherryPickResponse,
  RevertCommitResponse,
  ResetToCommitResponse,
} from '@openchamber/ui/lib/api/types';

type GitIdentityStoreState = {
  profiles: GitIdentityProfile[];
};

type GitIdentityStoreApi = {
  getState: () => GitIdentityStoreState;
  setState: (
    nextState: GitIdentityStoreState | ((state: GitIdentityStoreState) => GitIdentityStoreState),
    replace?: boolean
  ) => void;
};

const getGitIdentityStore = (): GitIdentityStoreApi | undefined => (
  window as Window & {
    __zustand_git_identities_store__?: GitIdentityStoreApi;
  }
).__zustand_git_identities_store__;

export const createVSCodeGitAPI = (): GitAPI => ({
  checkIsGitRepository: async (directory: string): Promise<boolean> => {
    return sendBridgeMessage<boolean>('api:git/check', { directory });
  },

  getGitStatus: async (directory: string, options?: { mode?: 'light' }): Promise<GitStatus> => {
    return sendBridgeMessage<GitStatus>('api:git/status', { directory, mode: options?.mode });
  },

  getGitDiff: async (directory: string, options: GetGitDiffOptions): Promise<GitDiffResponse> => {
    return sendBridgeMessage<GitDiffResponse>('api:git/diff', {
      directory,
      path: options.path,
      staged: options.staged,
      contextLines: options.contextLines,
    });
  },

  getGitFileDiff: async (directory: string, options: GetGitFileDiffOptions): Promise<GitFileDiffResponse> => {
    return sendBridgeMessage<GitFileDiffResponse>('api:git/file-diff', {
      directory,
      path: options.path,
      staged: options.staged,
    });
  },

  revertGitFile: async (directory: string, filePath: string, options?: { scope?: 'all' | 'working' }): Promise<void> => {
    await sendBridgeMessage('api:git/revert', { directory, path: filePath, scope: options?.scope });
  },

  stageGitFile: async (directory: string, filePath: string): Promise<void> => {
    await sendBridgeMessage('api:git/stage', { directory, path: filePath });
  },

  stageGitFiles: async (directory: string, filePaths: string[]): Promise<void> => {
    await sendBridgeMessage('api:git/stage', { directory, paths: filePaths });
  },

  unstageGitFile: async (directory: string, filePath: string): Promise<void> => {
    await sendBridgeMessage('api:git/unstage', { directory, path: filePath });
  },

  unstageGitFiles: async (directory: string, filePaths: string[]): Promise<void> => {
    await sendBridgeMessage('api:git/unstage', { directory, paths: filePaths });
  },

  stageGitHunk: async (directory: string, filePath: string, patch: string): Promise<void> => {
    await sendBridgeMessage('api:git/apply-hunk', { directory, path: filePath, patch, action: 'stage' });
  },

  unstageGitHunk: async (directory: string, filePath: string, patch: string): Promise<void> => {
    await sendBridgeMessage('api:git/apply-hunk', { directory, path: filePath, patch, action: 'unstage' });
  },

  revertGitHunk: async (directory: string, filePath: string, patch: string): Promise<void> => {
    await sendBridgeMessage('api:git/apply-hunk', { directory, path: filePath, patch, action: 'discard' });
  },

  isLinkedWorktree: async (directory: string): Promise<boolean> => {
    return sendBridgeMessage<boolean>('api:git/worktree-type', { directory });
  },

  getGitBranches: async (directory: string): Promise<GitBranch> => {
    return sendBridgeMessage<GitBranch>('api:git/branches', { directory, method: 'GET' });
  },

  deleteGitBranch: async (directory: string, payload: GitDeleteBranchPayload): Promise<{ success: boolean }> => {
    return sendBridgeMessage<{ success: boolean }>('api:git/branches', {
      directory,
      method: 'DELETE',
      name: payload.branch,
      force: payload.force,
    });
  },

  deleteRemoteBranch: async (directory: string, payload: GitDeleteRemoteBranchPayload): Promise<{ success: boolean }> => {
    return sendBridgeMessage<{ success: boolean }>('api:git/remote-branches', {
      directory,
      branch: payload.branch,
      remote: payload.remote,
    });
  },

  removeRemote: async (directory: string, payload: GitRemoveRemotePayload): Promise<{ success: boolean }> => {
    return sendBridgeMessage<{ success: boolean }>('api:git/remotes', {
      directory,
      method: 'DELETE',
      remote: payload.remote,
    });
  },

  generateCommitMessage: async (
    directory: string,
    files: string[],
    options?: { zenModel?: string; providerId?: string; modelId?: string }
  ): Promise<{ message: GeneratedCommitMessage }> => {
    // This requires AI integration - stubbed for now
    void directory; // Unused for now
    void files; // Unused for now
    void options; // Unused for now
    return {
      message: {
        subject: '',
        highlights: [],
      },
    };
  },

  generatePullRequestDescription: async (
    directory: string,
    payload: { base: string; head: string; context?: string; zenModel?: string; providerId?: string; modelId?: string }
  ): Promise<GeneratedPullRequestDescription> => {
    return sendBridgeMessage<GeneratedPullRequestDescription>('api:git/pr-description', {
      directory,
      base: payload.base,
      head: payload.head,
      context: payload.context,
      zenModel: payload.zenModel,
      providerId: payload.providerId,
      modelId: payload.modelId,
    });
  },

  listGitWorktrees: async (directory: string): Promise<GitWorktreeInfo[]> => {
    return sendBridgeMessage<GitWorktreeInfo[]>('api:git/worktrees', { directory, method: 'GET' });
  },

  validateGitWorktree: async (directory: string, payload: CreateGitWorktreePayload): Promise<GitWorktreeValidationResult> => {
    return sendBridgeMessage<GitWorktreeValidationResult>('api:git/worktrees/validate', {
      directory,
      ...(payload || {}),
    });
  },

  getGitWorktreeBootstrapStatus: async (directory: string): Promise<{ status: 'pending' | 'ready' | 'failed'; error: string | null; updatedAt: number }> => {
    return sendBridgeMessage<{ status: 'pending' | 'ready' | 'failed'; error: string | null; updatedAt: number }>('api:git/worktrees/bootstrap-status', {
      directory,
    });
  },

  previewGitWorktree: async (directory: string, payload: CreateGitWorktreePayload): Promise<GitWorktreeCreateResult> => {
    return sendBridgeMessage<GitWorktreeCreateResult>('api:git/worktrees/preview', {
      directory,
      method: 'POST',
      ...(payload || {}),
    });
  },

  createGitWorktree: async (directory: string, payload: CreateGitWorktreePayload): Promise<GitWorktreeCreateResult> => {
    return sendBridgeMessage<GitWorktreeCreateResult>('api:git/worktrees', {
      directory,
      method: 'POST',
      ...(payload || {}),
    });
  },

  deleteGitWorktree: async (directory: string, payload: RemoveGitWorktreePayload): Promise<{ success: boolean }> => {
    return sendBridgeMessage<{ success: boolean }>('api:git/worktrees', {
      directory,
      method: 'DELETE',
      body: {
        directory: payload.directory,
        deleteLocalBranch: payload.deleteLocalBranch === true,
      },
    });
  },

  createGitCommit: async (directory: string, message: string, options?: CreateGitCommitOptions): Promise<GitCommitResult> => {
    return sendBridgeMessage<GitCommitResult>('api:git/commit', {
      directory,
      message,
      addAll: options?.addAll,
      files: options?.files,
      stageFiles: options?.stageFiles,
    });
  },

  gitPush: async (directory: string, options?: { remote?: string; branch?: string; options?: string[] | Record<string, unknown> }): Promise<GitPushResult> => {
    return sendBridgeMessage<GitPushResult>('api:git/push', {
      directory,
      remote: options?.remote,
      branch: options?.branch,
      options: options?.options,
    });
  },

  gitPull: async (directory: string, options?: { remote?: string; branch?: string; rebase?: boolean }): Promise<GitPullResult> => {
    return sendBridgeMessage<GitPullResult>('api:git/pull', {
      directory,
      remote: options?.remote,
      branch: options?.branch,
      rebase: options?.rebase,
    });
  },

  gitFetch: async (directory: string, options?: { remote?: string; branch?: string }): Promise<{ success: boolean }> => {
    return sendBridgeMessage<{ success: boolean }>('api:git/fetch', {
      directory,
      remote: options?.remote,
      branch: options?.branch,
    });
  },

  listGitStashes: async (directory: string) => sendBridgeMessage('api:git/stashes', { directory }),
  countGitStashFiles: async (directory: string, refs: string[]) => sendBridgeMessage('api:git/stashes/file-counts', { directory, refs }),
  stashGitChanges: async (directory: string, options?: { message?: string }) => sendBridgeMessage('api:git/stash', { directory, message: options?.message }),
  applyGitStash: async (directory: string, options: { ref: string }) => sendBridgeMessage('api:git/stash/apply', { directory, ref: options.ref }),
  popGitStash: async (directory: string, options: { ref: string }) => sendBridgeMessage('api:git/stash/pop', { directory, ref: options.ref }),
  dropGitStash: async (directory: string, options: { ref: string }) => sendBridgeMessage('api:git/stash/drop', { directory, ref: options.ref }),

  checkoutBranch: async (directory: string, branch: string): Promise<{ success: boolean; branch: string }> => {
    return sendBridgeMessage<{ success: boolean; branch: string }>('api:git/checkout', {
      directory,
      branch,
    });
  },

  createBranch: async (directory: string, name: string, startPoint?: string): Promise<{ success: boolean; branch: string }> => {
    return sendBridgeMessage<{ success: boolean; branch: string }>('api:git/branches', {
      directory,
      method: 'POST',
      name,
      startPoint,
    });
  },

  renameBranch: async (directory: string, oldName: string, newName: string): Promise<{ success: boolean; branch: string }> => {
    return sendBridgeMessage<{ success: boolean; branch: string }>('api:git/branches/rename', {
      directory,
      method: 'PUT',
      oldName,
      newName,
    });
  },

  getGitLog: async (directory: string, options?: GitLogOptions): Promise<GitLogResponse> => {
    return sendBridgeMessage<GitLogResponse>('api:git/log', {
      directory,
      maxCount: options?.maxCount,
      from: options?.from,
      to: options?.to,
      file: options?.file,
      all: options?.all,
    });
  },

  getCommitFiles: async (directory: string, hash: string): Promise<GitCommitFilesResponse> => {
    return sendBridgeMessage<GitCommitFilesResponse>('api:git/commit-files', {
      directory,
      hash,
    });
  },

  getCommitFileDiff: async (directory: string, hash: string, filePath: string, isBinary: boolean): Promise<CommitFileDiffResponse> => {
    return sendBridgeMessage<CommitFileDiffResponse>('api:git/commit-file-diff', {
      directory,
      hash,
      path: filePath,
      binary: isBinary,
    });
  },

  getCurrentGitIdentity: async (directory: string): Promise<GitIdentitySummary | null> => {
    return sendBridgeMessage<GitIdentitySummary | null>('api:git/identity', {
      directory,
      method: 'GET',
    });
  },

  setGitIdentity: async (directory: string, profileId: string): Promise<{ success: boolean; profile: GitIdentityProfile }> => {
    const store = (window as Window & {
      __zustand_git_identities_store__?: {
        getState: () => {
          getProfileById: (id: string) => GitIdentityProfile | undefined;
        };
      };
    }).__zustand_git_identities_store__;
    const profile = store?.getState().getProfileById(profileId);
    if (!profile) {
      return {
        success: false,
        profile: { id: profileId, name: '', userName: '', userEmail: '' },
      };
    }

    const result = await sendBridgeMessage<{ success: boolean }>('api:git/identity', {
      directory,
      method: 'POST',
      userName: profile.userName,
      userEmail: profile.userEmail,
      sshKey: profile.sshKey ?? null,
      signCommits: profile.signCommits === true,
      signingKey: profile.signingKey ?? null,
    });

    return {
      success: result.success === true,
      profile,
    };
  },

  // Git identity profile management is backed by the webview store in VS Code.
  getGitIdentities: async (): Promise<GitIdentityProfile[]> => {
    return getGitIdentityStore()?.getState().profiles ?? [];
  },

  createGitIdentity: async (profile: GitIdentityProfile): Promise<GitIdentityProfile> => {
    const store = getGitIdentityStore();
    if (store) {
      store.setState((state) => ({
        profiles: [...state.profiles.filter((existing) => existing.id !== profile.id), profile],
      }));
    }
    return profile;
  },

  updateGitIdentity: async (id: string, profile: GitIdentityProfile): Promise<GitIdentityProfile> => {
    const store = getGitIdentityStore();
    if (store) {
      store.setState((state) => ({
        profiles: state.profiles.map((existing) => (existing.id === id ? { ...existing, ...profile, id } : existing)),
      }));
    }
    return profile;
  },

  deleteGitIdentity: async (id: string): Promise<void> => {
    const store = getGitIdentityStore();
    if (store) {
      store.setState((state) => ({
        profiles: state.profiles.filter((existing) => existing.id !== id),
      }));
    }
  },

  getRemotes: async (directory: string): Promise<GitRemote[]> => {
    return sendBridgeMessage<GitRemote[]>('api:git/remotes', { directory });
  },

  rebase: async (directory: string, options: { onto: string }): Promise<GitRebaseResult> => {
    return sendBridgeMessage<GitRebaseResult>('api:git/rebase', {
      directory,
      onto: options.onto,
    });
  },

  abortRebase: async (directory: string): Promise<{ success: boolean }> => {
    return sendBridgeMessage<{ success: boolean }>('api:git/rebase/abort', { directory });
  },

  merge: async (directory: string, options: { branch: string }): Promise<GitMergeResult> => {
    return sendBridgeMessage<GitMergeResult>('api:git/merge', {
      directory,
      branch: options.branch,
    });
  },

  abortMerge: async (directory: string): Promise<{ success: boolean }> => {
    return sendBridgeMessage<{ success: boolean }>('api:git/merge/abort', { directory });
  },

  continueRebase: async (directory: string): Promise<{ success: boolean; conflict: boolean; conflictFiles?: string[] }> => {
    return sendBridgeMessage<{ success: boolean; conflict: boolean; conflictFiles?: string[] }>('api:git/rebase/continue', { directory });
  },

  continueMerge: async (directory: string): Promise<{ success: boolean; conflict: boolean; conflictFiles?: string[] }> => {
    return sendBridgeMessage<{ success: boolean; conflict: boolean; conflictFiles?: string[] }>('api:git/merge/continue', { directory });
  },

  checkoutCommit: async (directory: string, hash: string): Promise<CheckoutCommitResponse> => {
    return sendBridgeMessage<CheckoutCommitResponse>('api:git/checkout-commit', { directory, hash });
  },

  cherryPick: async (directory: string, hash: string): Promise<CherryPickResponse> => {
    return sendBridgeMessage<CherryPickResponse>('api:git/cherry-pick', { directory, hash });
  },

  revertCommit: async (directory: string, hash: string): Promise<RevertCommitResponse> => {
    return sendBridgeMessage<RevertCommitResponse>('api:git/revert-commit', { directory, hash });
  },

  resetToCommit: async (directory: string, hash: string, mode: 'soft' | 'mixed' | 'hard', force?: boolean): Promise<ResetToCommitResponse> => {
    return sendBridgeMessage<ResetToCommitResponse>('api:git/reset-to-commit', { directory, hash, mode, force });
  },

  stash: async (
    directory: string,
    options?: { message?: string; includeUntracked?: boolean }
  ): Promise<{ success: boolean }> => {
    return sendBridgeMessage<{ success: boolean }>('api:git/stash', {
      directory,
      ...options,
    });
  },

  stashPop: async (directory: string): Promise<{ success: boolean }> => {
    return sendBridgeMessage<{ success: boolean }>('api:git/stash/pop', { directory });
  },

  getConflictDetails: async (directory: string) => {
    return sendBridgeMessage<{
      statusPorcelain: string;
      unmergedFiles: string[];
      diff: string;
      headInfo: string;
      operation: 'merge' | 'rebase';
    }>('api:git/conflict-details', { directory });
  },

  validateWorktreeDirectory: async (directory: string, worktreeRoot: string): Promise<{
    valid: boolean;
    insideWorktreeRoot: boolean;
    resolvedWorktreeRoot: string | null;
    resolvedCwd: string | null;
  }> => {
    return sendBridgeMessage<{
      valid: boolean;
      insideWorktreeRoot: boolean;
      resolvedWorktreeRoot: string | null;
      resolvedCwd: string | null;
    }>('api:git/validate-directory', { directory, worktreeRoot });
  },

  canonicalizeWorktreeState: async (directory: string): Promise<{
    worktreeRoot: string | null;
    cwd: string | null;
    branch: string | null;
    headState: 'branch' | 'detached' | 'unborn';
    worktreeStatus: 'ready' | 'missing' | 'invalid' | 'not-a-repo';
    legacy: boolean;
    degraded: boolean;
    attentionReason?: 'merge' | 'rebase' | 'cherry-pick' | 'revert' | 'bisect' | null;
  }> => {
    return sendBridgeMessage<{
      worktreeRoot: string | null;
      cwd: string | null;
      branch: string | null;
      headState: 'branch' | 'detached' | 'unborn';
      worktreeStatus: 'ready' | 'missing' | 'invalid' | 'not-a-repo';
      legacy: boolean;
      degraded: boolean;
      attentionReason?: 'merge' | 'rebase' | 'cherry-pick' | 'revert' | 'bisect' | null;
    }>('api:git/canonicalize-worktree-state', { directory });
  },

  worktree: {
    list: async (directory: string): Promise<GitWorktreeInfo[]> => {
      return sendBridgeMessage<GitWorktreeInfo[]>('api:git/worktrees', { directory, method: 'GET' });
    },
    validate: async (directory: string, payload: CreateGitWorktreePayload): Promise<GitWorktreeValidationResult> => {
      return sendBridgeMessage<GitWorktreeValidationResult>('api:git/worktrees/validate', {
        directory,
        ...(payload || {}),
      });
    },
    bootstrapStatus: async (directory: string): Promise<{ status: 'pending' | 'ready' | 'failed'; error: string | null; updatedAt: number }> => {
      return sendBridgeMessage<{ status: 'pending' | 'ready' | 'failed'; error: string | null; updatedAt: number }>('api:git/worktrees/bootstrap-status', {
        directory,
      });
    },
    preview: async (directory: string, payload: CreateGitWorktreePayload): Promise<GitWorktreeCreateResult> => {
      return sendBridgeMessage<GitWorktreeCreateResult>('api:git/worktrees/preview', {
        directory,
        method: 'POST',
        ...(payload || {}),
      });
    },
    create: async (directory: string, payload: CreateGitWorktreePayload): Promise<GitWorktreeCreateResult> => {
      return sendBridgeMessage<GitWorktreeCreateResult>('api:git/worktrees', {
        directory,
        method: 'POST',
        ...(payload || {}),
      });
    },
    remove: async (directory: string, payload: RemoveGitWorktreePayload): Promise<{ success: boolean }> => {
      return sendBridgeMessage<{ success: boolean }>('api:git/worktrees', {
        directory,
        method: 'DELETE',
        body: {
          directory: payload.directory,
          deleteLocalBranch: payload.deleteLocalBranch === true,
        },
      });
    },
  },
});
