import { Injectable, Logger } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { simpleGit, SimpleGit } from "simple-git";
import { GitOperationError } from "./types";

/**
 * Service for managing git operations
 */
@Injectable()
export class GitOperationsService {
  private readonly logger = new Logger(GitOperationsService.name);
  private readonly gitUserName: string;
  private readonly gitUserEmail: string;

  constructor(private readonly configService: ConfigService) {
    this.gitUserName =
      this.configService.get<string>("orchestrator.git.userName") ?? "Mosaic Orchestrator";
    this.gitUserEmail =
      this.configService.get<string>("orchestrator.git.userEmail") ??
      "orchestrator@mosaicstack.dev";
  }

  /**
   * Get a simple-git instance for a local path
   */
  private getGit(localPath: string): SimpleGit {
    return simpleGit(localPath);
  }

  /**
   * Clone a repository
   */
  async cloneRepository(url: string, localPath: string, branch?: string): Promise<void> {
    try {
      this.logger.log(`Cloning repository ${url} to ${localPath}`);
      const git = simpleGit();

      if (branch) {
        await git.clone(url, localPath, ["--branch", branch]);
      } else {
        await git.clone(url, localPath);
      }

      this.logger.log(`Successfully cloned repository to ${localPath}`);
    } catch (error) {
      this.logger.error(`Failed to clone repository: ${String(error)}`);
      throw new GitOperationError(
        `Failed to clone repository from ${url}`,
        "clone",
        error as Error
      );
    }
  }

  /**
   * Create a new branch
   */
  async createBranch(localPath: string, branchName: string): Promise<void> {
    try {
      this.logger.log(`Creating branch ${branchName} at ${localPath}`);
      const git = this.getGit(localPath);

      await git.checkoutLocalBranch(branchName);

      this.logger.log(`Successfully created branch ${branchName}`);
    } catch (error) {
      this.logger.error(`Failed to create branch: ${String(error)}`);
      throw new GitOperationError(
        `Failed to create branch ${branchName}`,
        "createBranch",
        error as Error
      );
    }
  }

  /**
   * Commit changes
   */
  async commit(localPath: string, message: string, files?: string[]): Promise<void> {
    try {
      this.logger.log(`Committing changes at ${localPath}`);
      const git = this.getGit(localPath);

      // Configure git user
      await git.addConfig("user.name", this.gitUserName);
      await git.addConfig("user.email", this.gitUserEmail);

      // Stage files
      if (files && files.length > 0) {
        await git.add(files);
      } else {
        await git.add(".");
      }

      // Commit
      await git.commit(message);

      this.logger.log(`Successfully committed changes: ${message}`);
    } catch (error) {
      this.logger.error(`Failed to commit: ${String(error)}`);
      throw new GitOperationError(`Failed to commit changes`, "commit", error as Error);
    }
  }

  /**
   * Push changes to remote
   */
  async push(localPath: string, remote = "origin", branch?: string, force = false): Promise<void> {
    try {
      this.logger.log(`Pushing changes from ${localPath} to ${remote}`);
      const git = this.getGit(localPath);

      if (force) {
        await git.push(remote, branch, { "--force": null });
      } else {
        await git.push(remote, branch);
      }

      this.logger.log(`Successfully pushed changes to ${remote}`);
    } catch (error) {
      this.logger.error(`Failed to push: ${String(error)}`);
      throw new GitOperationError(`Failed to push changes to ${remote}`, "push", error as Error);
    }
  }
}
