/**
 * sync CLI execute — Execution logic, no validation
 */

import type { SyncSpec, SyncResult } from './types.js';
import { readConfig } from '../lib/config.js';
import { getBaseBranch, detectBranchType } from '../lib/branch.js';
import { getCurrentBranch, fetchAll, getAheadBehind, push, getStatus, stash, pull, stashPop, rebase } from '../lib/git.js';
import { ensureWorkTreeUsable } from '../lib/worktree.js';

export async function execute(spec: SyncSpec, cwd?: string): Promise<SyncResult> {
  try {
    cwd = cwd ?? process.cwd();
    const config = await readConfig();
    const branch = await getCurrentBranch(cwd);
    const branchType = detectBranchType(branch);
    const baseBranch = getBaseBranch(branchType, config);

    let pushed = false;
    let pulled = false;
    let rebased = false;
    let conflicts = false;
    let conflictFiles: string[] = [];

    // Repair a worktree git wrongly treats as bare before any work-tree op.
    const usable = await ensureWorkTreeUsable(cwd);
    if (!usable.ok) {
      return { success: false, error: usable.error, branch, baseBranch, pushed: false, pulled: false, rebased: false, conflicts: false };
    }

    // A failed fetch must surface, not be swallowed → never compare/sync against
    // STALE refs (the old fetchAll returned void → silent stale comparisons).
    const fetched = await fetchAll(cwd);
    if (fetched.exitCode !== 0) {
      return { success: false, error: `git fetch failed: ${fetched.stderr || 'unknown error'}`, branch, baseBranch, pushed: false, pulled: false, rebased: false, conflicts: false };
    }

    const aheadBehind = await getAheadBehind(branch, `origin/${branch}`, cwd);
    if (aheadBehind.ahead > 0) {
      await push(branch, cwd);
      pushed = true;
    }

    if (aheadBehind.behind > 0) {
      const status = await getStatus(cwd);

      if (status.dirty) {
        await stash(cwd);
      }

      const pullResult = await pull('origin', branch, cwd);
      pulled = pullResult.exitCode === 0;

      if (status.dirty) {
        const popResult = await stashPop(cwd);
        if (popResult.exitCode !== 0) {
          // Stash pop failed - might have conflicts
          conflicts = true;
        }
      }
    }

    if (spec.rebase) {
      const baseAheadBehind = await getAheadBehind(branch, `origin/${baseBranch}`, cwd);
      if (baseAheadBehind.behind > 0) {
        const rebaseResult = await rebase(baseBranch, cwd);
        rebased = rebaseResult.success;
        conflicts = !rebaseResult.success;
        if (!rebaseResult.success) {
          conflictFiles = rebaseResult.conflicts;
        }
      }
    }

    return {
      success: !conflicts,
      branch,
      baseBranch,
      pushed,
      pulled,
      rebased,
      conflicts,
      conflictFiles: conflicts ? conflictFiles : undefined,
    };
  } catch (err: unknown) {
    return {
      success: false,
      error: (err as Error).message,
      branch: '',
      baseBranch: '',
      pushed: false,
      pulled: false,
      rebased: false,
      conflicts: true,
    };
  }
}
