import { execa } from 'execa';
import type { BitbucketConfig } from '../types';

export class BitbucketProvider {
  private readonly config: BitbucketConfig;
  private readonly projectRoot: string;

  constructor(config: BitbucketConfig, projectRoot: string) {
    this.config = config;
    this.projectRoot = projectRoot;
  }

  /**
   * Pushes a local branch to the remote origin.
   * The worktree / local branch is expected to have been created by the sdd CLI
   * before this method is called.
   */
  async pushBranch(branch: string): Promise<void> {
    try {
      await execa('git', ['push', 'origin', branch], {
        cwd: this.projectRoot,
        reject: true,
      });
    } catch (err: unknown) {
      const msg = this._extractMessage(err);
      throw new Error(
        `BitbucketProvider.pushBranch: failed to push branch "${branch}" from "${this.projectRoot}": ${msg}`
      );
    }
  }

  /**
   * Checks whether the branch already exists on the remote.
   * If it does not exist, pushes it.
   */
  async ensureRemoteBranch(branch: string): Promise<void> {
    const exists = await this._remoteBranchExists(branch);
    if (exists) return;
    await this.pushBranch(branch);
  }

  // ── Internal helpers ──────────────────────────────────────────────────────

  private async _remoteBranchExists(branch: string): Promise<boolean> {
    try {
      const result = await execa(
        'git',
        ['ls-remote', '--heads', 'origin', branch],
        { cwd: this.projectRoot, reject: true }
      );
      // ls-remote prints one line per matching ref; empty stdout means not found
      return result.stdout.trim().length > 0;
    } catch (err: unknown) {
      const msg = this._extractMessage(err);
      throw new Error(
        `BitbucketProvider._remoteBranchExists: failed to query remote for branch "${branch}": ${msg}`
      );
    }
  }

  private _extractMessage(err: unknown): string {
    if (
      typeof err === 'object' &&
      err !== null &&
      'stderr' in err &&
      typeof (err as { stderr?: unknown }).stderr === 'string'
    ) {
      const stderr = (err as { stderr: string }).stderr.trim();
      if (stderr) return stderr;
    }
    return (err as Error).message ?? JSON.stringify(err);
  }
}
