/**
 * SimpleGitBranchManager Tests
 *
 * Tests for remoteBranchExists validation used when setting
 * a project's default branch.
 */

import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { mkdtemp, rm, writeFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import path from 'node:path';
import { simpleGit } from 'simple-git';
import { SimpleGitBranchManager } from './simple-git-branch-manager.js';

/**
 * Create a "remote" bare repo and a clone of it for testing.
 * The bare repo has branches that the clone can see as remote tracking branches.
 */
async function createTestRepoWithRemote(): Promise<{
  clonePath: string;
  barePath: string;
  cleanup: () => Promise<void>;
}> {
  const tmpBase = await mkdtemp(path.join(tmpdir(), 'cw-branch-test-'));
  const barePath = path.join(tmpBase, 'bare.git');
  const workPath = path.join(tmpBase, 'work');
  const clonePath = path.join(tmpBase, 'clone');

  // Create a bare repo
  const bareGit = simpleGit();
  await bareGit.init([barePath, '--bare']);

  // Clone it to a working directory, add commits and branches, push
  await simpleGit().clone(barePath, workPath);
  const workGit = simpleGit(workPath);
  await workGit.addConfig('user.email', 'test@example.com');
  await workGit.addConfig('user.name', 'Test User');
  await writeFile(path.join(workPath, 'README.md'), '# Test\n');
  await workGit.add('README.md');
  await workGit.commit('Initial commit');
  await workGit.push('origin', 'main');

  // Create additional branches
  await workGit.checkoutLocalBranch('develop');
  await writeFile(path.join(workPath, 'dev.txt'), 'dev\n');
  await workGit.add('dev.txt');
  await workGit.commit('Dev commit');
  await workGit.push('origin', 'develop');

  await workGit.checkoutLocalBranch('feature/auth');
  await writeFile(path.join(workPath, 'auth.txt'), 'auth\n');
  await workGit.add('auth.txt');
  await workGit.commit('Auth commit');
  await workGit.push('origin', 'feature/auth');

  // Clone from bare to simulate what project registration does
  await simpleGit().clone(barePath, clonePath);

  return {
    clonePath,
    barePath,
    cleanup: async () => {
      await rm(tmpBase, { recursive: true, force: true });
    },
  };
}

describe('SimpleGitBranchManager', () => {
  let clonePath: string;
  let cleanup: () => Promise<void>;
  let branchManager: SimpleGitBranchManager;

  beforeEach(async () => {
    const setup = await createTestRepoWithRemote();
    clonePath = setup.clonePath;
    cleanup = setup.cleanup;
    branchManager = new SimpleGitBranchManager();
  });

  afterEach(async () => {
    await cleanup();
  });

  describe('remoteBranchExists', () => {
    it('should return true for a branch that exists on the remote', async () => {
      expect(await branchManager.remoteBranchExists(clonePath, 'main')).toBe(true);
      expect(await branchManager.remoteBranchExists(clonePath, 'develop')).toBe(true);
      expect(await branchManager.remoteBranchExists(clonePath, 'feature/auth')).toBe(true);
    });

    it('should return false for a branch that does not exist', async () => {
      expect(await branchManager.remoteBranchExists(clonePath, 'nonexistent')).toBe(false);
      expect(await branchManager.remoteBranchExists(clonePath, 'feature/nope')).toBe(false);
    });

    it('should return false for an invalid repo path', async () => {
      expect(await branchManager.remoteBranchExists('/tmp/no-such-repo', 'main')).toBe(false);
    });

    it('should detect remote branches not checked out locally', async () => {
      // After clone, only 'main' is checked out locally.
      // 'develop' exists only as origin/develop.
      const localExists = await branchManager.branchExists(clonePath, 'develop');
      const remoteExists = await branchManager.remoteBranchExists(clonePath, 'develop');

      expect(localExists).toBe(false);
      expect(remoteExists).toBe(true);
    });
  });
});
