import { execFileSync } from 'node:child_process';
import { createLogger } from '../../shared/utils/index.js';
import { resolveConfigValue } from '../config/index.js';
import { detectDefaultBranch } from './branchList.js';
import { runGitCommandAbortable } from './clone-exec.js';
const log = createLogger('clone');
export function localBranchExists(projectDir, branch) {
    try {
        execFileSync('git', ['show-ref', '--verify', '--quiet', `refs/heads/${branch}`], {
            cwd: projectDir,
            stdio: 'pipe',
        });
        return true;
    }
    catch {
        return false;
    }
}
export function remoteBranchExists(projectDir, branch) {
    try {
        execFileSync('git', ['show-ref', '--verify', '--quiet', `refs/remotes/origin/${branch}`], {
            cwd: projectDir,
            stdio: 'pipe',
        });
        return true;
    }
    catch {
        return false;
    }
}
export function branchExists(projectDir, branch) {
    return localBranchExists(projectDir, branch) || remoteBranchExists(projectDir, branch);
}
export async function localBranchExistsAbortable(projectDir, branch, abortSignal) {
    try {
        await runGitCommandAbortable(projectDir, ['show-ref', '--verify', '--quiet', `refs/heads/${branch}`], abortSignal);
        return true;
    }
    catch {
        return false;
    }
}
export async function remoteBranchExistsAbortable(projectDir, branch, abortSignal) {
    try {
        await runGitCommandAbortable(projectDir, ['show-ref', '--verify', '--quiet', `refs/remotes/origin/${branch}`], abortSignal);
        return true;
    }
    catch {
        return false;
    }
}
export async function branchExistsAbortable(projectDir, branch, abortSignal) {
    return (await localBranchExistsAbortable(projectDir, branch, abortSignal)
        || await remoteBranchExistsAbortable(projectDir, branch, abortSignal));
}
function resolveConfiguredBaseBranch(projectDir, explicitBaseBranch) {
    if (explicitBaseBranch !== undefined) {
        const normalized = explicitBaseBranch.trim();
        if (normalized.length === 0) {
            throw new Error('Base branch override must not be empty.');
        }
        return normalized;
    }
    return resolveConfigValue(projectDir, 'baseBranch');
}
function assertValidBranchRef(projectDir, ref) {
    try {
        execFileSync('git', ['check-ref-format', '--branch', ref], {
            cwd: projectDir,
            stdio: 'pipe',
        });
    }
    catch {
        throw new Error(`Invalid base branch: ${ref}`);
    }
}
function assertNotRemoteTrackingRef(ref) {
    if (ref.startsWith('origin/')) {
        throw new Error(`Base branch must be a branch name, not a remote-tracking ref: ${ref}`);
    }
    if (ref.startsWith('refs/remotes/')) {
        throw new Error(`Base branch must be a branch name, not a remote-tracking ref: ${ref}`);
    }
}
function assertExplicitBaseBranch(projectDir, branch) {
    assertNotRemoteTrackingRef(branch);
    assertValidBranchRef(projectDir, branch);
}
function resolveBaseBranchStartPoint(projectDir, branch) {
    if (localBranchExists(projectDir, branch)) {
        return branch;
    }
    if (remoteBranchExists(projectDir, branch)) {
        return `origin/${branch}`;
    }
    throw new Error(`Base branch source does not exist: ${branch}`);
}
export function createBaseBranchIfMissing(projectDir, config) {
    assertExplicitBaseBranch(projectDir, config.name);
    assertExplicitBaseBranch(projectDir, config.create_if_missing.from);
    if (branchExists(projectDir, config.name)) {
        return { branch: config.name, created: false };
    }
    const startPoint = resolveBaseBranchStartPoint(projectDir, config.create_if_missing.from);
    execFileSync('git', ['branch', config.name, startPoint], {
        cwd: projectDir,
        stdio: 'pipe',
    });
    if (config.create_if_missing.push === true) {
        execFileSync('git', ['push', 'origin', config.name], {
            cwd: projectDir,
            stdio: 'pipe',
        });
    }
    return { branch: config.name, created: true };
}
export function resolveBaseBranch(projectDir, explicitBaseBranch) {
    const configBaseBranch = resolveConfiguredBaseBranch(projectDir, explicitBaseBranch);
    const autoFetch = resolveConfigValue(projectDir, 'autoFetch');
    const baseBranch = configBaseBranch ?? detectDefaultBranch(projectDir);
    if (explicitBaseBranch !== undefined) {
        assertExplicitBaseBranch(projectDir, baseBranch);
    }
    if (explicitBaseBranch !== undefined && !branchExists(projectDir, baseBranch)) {
        throw new Error(`Base branch does not exist: ${baseBranch}`);
    }
    if (!autoFetch) {
        return { branch: baseBranch };
    }
    try {
        execFileSync('git', ['fetch', 'origin'], {
            cwd: projectDir,
            stdio: 'pipe',
        });
        const fetchedCommit = execFileSync('git', ['rev-parse', `origin/${baseBranch}`], { cwd: projectDir, encoding: 'utf-8', stdio: 'pipe' }).trim();
        log.info('Fetched remote and resolved base branch', { baseBranch, fetchedCommit });
        return { branch: baseBranch, fetchedCommit };
    }
    catch (err) {
        log.info('Failed to fetch from remote, continuing with local state', { baseBranch, error: String(err) });
        return { branch: baseBranch };
    }
}
export async function resolveBaseBranchAbortable(projectDir, explicitBaseBranch, abortSignal) {
    const configBaseBranch = resolveConfiguredBaseBranch(projectDir, explicitBaseBranch);
    const autoFetch = resolveConfigValue(projectDir, 'autoFetch');
    const baseBranch = configBaseBranch ?? detectDefaultBranch(projectDir);
    if (explicitBaseBranch !== undefined) {
        assertExplicitBaseBranch(projectDir, baseBranch);
    }
    if (explicitBaseBranch !== undefined && !await branchExistsAbortable(projectDir, baseBranch, abortSignal)) {
        throw new Error(`Base branch does not exist: ${baseBranch}`);
    }
    if (!autoFetch) {
        return { branch: baseBranch };
    }
    try {
        await runGitCommandAbortable(projectDir, ['fetch', 'origin'], abortSignal);
        const { stdout } = await runGitCommandAbortable(projectDir, ['rev-parse', `origin/${baseBranch}`], abortSignal);
        const fetchedCommit = stdout.trim();
        log.info('Fetched remote and resolved base branch', { baseBranch, fetchedCommit });
        return { branch: baseBranch, fetchedCommit };
    }
    catch (err) {
        log.info('Failed to fetch from remote, continuing with local state', { baseBranch, error: String(err) });
        return { branch: baseBranch };
    }
}
//# sourceMappingURL=clone-base-branch.js.map