import { describe, it, expect, vi, beforeEach } from 'vitest';
import { GitApiAdapter } from '../../src/main/engine/GitApiAdapter';

const { mockGitEngine, mockProjectEngine } = vi.hoisted(() => ({
  mockGitEngine: {
    checkAvailability: vi.fn().mockResolvedValue({ gitFound: true, version: '2.40.0' }),
    getRepoState: vi.fn().mockResolvedValue({ isRepo: true, currentBranch: 'main', hasRemote: true }),
    getStatus: vi.fn().mockResolvedValue({ files: [], counts: { untracked: 0, modified: 0, deleted: 0, renamed: 0, staged: 0 } }),
    getHistory: vi.fn().mockResolvedValue([]),
    getRemoteState: vi.fn().mockResolvedValue({ localBranch: 'main', upstreamBranch: 'origin/main', hasUpstream: true, ahead: 0, behind: 0 }),
    fetch: vi.fn().mockResolvedValue({ success: true }),
    pull: vi.fn().mockResolvedValue({ success: true }),
    push: vi.fn().mockResolvedValue({ success: true }),
    commitAll: vi.fn().mockResolvedValue({ success: true }),
  },
  mockProjectEngine: {
    getActiveProject: vi.fn().mockResolvedValue({ id: 'p1', dataPath: '/projects/blog' }),
  },
}));

vi.mock('../../src/main/engine/GitEngine', () => ({
  getGitEngine: () => mockGitEngine,
}));

vi.mock('../../src/main/engine/ProjectEngine', () => ({
  getProjectEngine: () => mockProjectEngine,
}));

describe('GitApiAdapter', () => {
  let adapter: GitApiAdapter;

  beforeEach(() => {
    vi.clearAllMocks();
    adapter = new GitApiAdapter(mockGitEngine as any, mockProjectEngine as any);
  });

  it('checkAvailability delegates directly (no projectPath)', async () => {
    const result = await adapter.checkAvailability();
    expect(mockGitEngine.checkAvailability).toHaveBeenCalledWith();
    expect(result).toEqual({ gitFound: true, version: '2.40.0' });
  });

  it('getRepoState resolves projectPath from active project', async () => {
    await adapter.getRepoState();
    expect(mockProjectEngine.getActiveProject).toHaveBeenCalled();
    expect(mockGitEngine.getRepoState).toHaveBeenCalledWith('/projects/blog');
  });

  it('getStatus resolves projectPath', async () => {
    await adapter.getStatus();
    expect(mockGitEngine.getStatus).toHaveBeenCalledWith('/projects/blog');
  });

  it('getHistory passes limit and resolves projectPath', async () => {
    await adapter.getHistory(10);
    expect(mockGitEngine.getHistory).toHaveBeenCalledWith('/projects/blog', 10);
  });

  it('getHistory passes undefined limit', async () => {
    await adapter.getHistory();
    expect(mockGitEngine.getHistory).toHaveBeenCalledWith('/projects/blog', undefined);
  });

  it('getRemoteState resolves projectPath', async () => {
    await adapter.getRemoteState();
    expect(mockGitEngine.getRemoteState).toHaveBeenCalledWith('/projects/blog');
  });

  it('fetch resolves projectPath', async () => {
    await adapter.fetch();
    expect(mockGitEngine.fetch).toHaveBeenCalledWith('/projects/blog');
  });

  it('pull resolves projectPath', async () => {
    await adapter.pull();
    expect(mockGitEngine.pull).toHaveBeenCalledWith('/projects/blog');
  });

  it('push resolves projectPath', async () => {
    await adapter.push();
    expect(mockGitEngine.push).toHaveBeenCalledWith('/projects/blog');
  });

  it('commitAll resolves projectPath and passes message', async () => {
    await adapter.commitAll('update files');
    expect(mockGitEngine.commitAll).toHaveBeenCalledWith('/projects/blog', 'update files');
  });

  it('throws when no active project', async () => {
    mockProjectEngine.getActiveProject.mockResolvedValueOnce(null);
    await expect(adapter.getRepoState()).rejects.toThrow('No active project with a data path');
  });

  it('throws when active project has no dataPath', async () => {
    mockProjectEngine.getActiveProject.mockResolvedValueOnce({ id: 'p1', dataPath: null });
    await expect(adapter.fetch()).rejects.toThrow('No active project with a data path');
  });
});
