'use strict';

var rest = require('@octokit/rest');
var integration = require('@backstage/integration');
var YAML = require('js-yaml');

function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }

var YAML__default = /*#__PURE__*/_interopDefaultCompat(YAML);

class GithubWorkflowsService {
  integrations;
  credentialsProvider;
  logger;
  constructor(configApi, logger) {
    this.integrations = integration.ScmIntegrations.fromConfig(configApi);
    this.credentialsProvider = integration.DefaultGithubCredentialsProvider.fromIntegrations(this.integrations);
    this.logger = logger;
  }
  /**
   * Create an Octokit instance authenticated for the given repository URL.
   * Uses ScmIntegrations to handle GitHub Apps and tokens automatically.
   * 
   * IMPORTANT: For GitHub Apps, repoSlug is REQUIRED to select the correct installation.
   */
  async getOctokit(hostname = "github.com", repoSlug) {
    const targetUrl = `https://${hostname}/${repoSlug}`;
    const ghIntegration = this.integrations.github.byUrl(targetUrl);
    if (!ghIntegration) {
      throw new Error(`No GitHub integration configured for host: ${hostname}`);
    }
    const credential = await this.credentialsProvider.getCredentials({ url: targetUrl });
    const token = credential?.token;
    const headers = credential?.headers || {};
    if (!token) {
      throw new Error(`No token available for ${hostname}`);
    }
    let authType = "Personal Access Token";
    if (token.startsWith("ghs_")) {
      authType = "GitHub App installation token";
    } else if (token.startsWith("github_pat_")) {
      authType = "Fine-grained PAT";
    }
    const baseUrl = ghIntegration.config.apiBaseUrl;
    this.logger.info(`GitHub integration using ${authType} for ${hostname}/${repoSlug} (baseUrl: ${baseUrl || "default"})`);
    return new rest.Octokit({
      auth: token,
      baseUrl,
      userAgent: headers?.["user-agent"] || "backstage-github-workflows-plugin",
      request: { headers }
    });
  }
  parseRepo(githubRepoSlug) {
    const parse = githubRepoSlug.split("/");
    return { owner: parse[0], repo: parse[1] };
  }
  regexFileName(path) {
    const parts = path.split("/");
    return parts[parts.length - 1];
  }
  /**
   * Wraps Octokit calls to catch errors and re-throw them as plain Error objects
   * This prevents Backstage's error serialization from accessing the deprecated error.code property
   */
  async catchOctokitError(fn) {
    try {
      return await fn();
    } catch (error) {
      const message = error.message || "GitHub API request failed";
      const status = error.status || error.statusCode || 500;
      throw new Error(`GitHub API Error (${status}): ${message}`);
    }
  }
  async listWorkflows(hostname, githubRepoSlug, branch, filter) {
    return this.catchOctokitError(async () => {
      const octokit = await this.getOctokit(hostname, githubRepoSlug);
      const { owner, repo } = this.parseRepo(githubRepoSlug);
      const response = await octokit.actions.listRepoWorkflows({
        owner,
        repo
      });
      let workflows = response.data.workflows;
      const excludedWorkflows = ["Dependabot Updates", "CodeQL"];
      workflows = workflows.filter(
        (workflow) => !excludedWorkflows.includes(workflow.name)
      );
      if (filter && filter.length > 0) {
        workflows = workflows.filter(
          (workflow) => filter.includes(this.regexFileName(workflow.path))
        );
      }
      const detailedWorkflows = await Promise.all(
        workflows.map(async (workflow) => {
          const latestRun = await this.getLatestWorkflowRun(hostname, workflow.id, githubRepoSlug);
          const dispatchParameters = await this.listWorkflowsDispatchParameters(
            hostname,
            githubRepoSlug,
            workflow.path,
            branch
          );
          const latestWorkflowRunData = latestRun ? {
            id: latestRun.id,
            status: latestRun.status ?? void 0,
            conclusion: latestRun.conclusion ?? void 0
          } : {
            status: "completed",
            conclusion: "failure"
          };
          return {
            workflow: {
              id: workflow.id,
              name: workflow.name,
              state: workflow.state,
              url: workflow.html_url,
              path: workflow.path,
              createdAt: workflow.created_at,
              updatedAt: workflow.updated_at
            },
            latestRun: {
              ...latestWorkflowRunData,
              status: workflow.path.startsWith("dynamic/") ? "action_required" : latestWorkflowRunData.status
            },
            parameters: dispatchParameters
          };
        })
      );
      return detailedWorkflows;
    });
  }
  async listBranchesFromRepo(hostname, githubRepoSlug) {
    return this.catchOctokitError(async () => {
      const octokit = await this.getOctokit(hostname, githubRepoSlug);
      const { owner, repo } = this.parseRepo(githubRepoSlug);
      const response = await octokit.rest.repos.listBranches({
        owner,
        repo
      });
      return response.data;
    });
  }
  async getBranchDefaultFromRepo(hostname, githubRepoSlug) {
    return this.catchOctokitError(async () => {
      const octokit = await this.getOctokit(hostname, githubRepoSlug);
      const { owner, repo } = this.parseRepo(githubRepoSlug);
      const response = await octokit.rest.repos.get({
        owner,
        repo
      });
      return response.data.default_branch;
    });
  }
  async startWorkflowRun(hostname, githubRepoSlug, workflowId, branch, inputs) {
    return this.catchOctokitError(async () => {
      const octokit = await this.getOctokit(hostname, githubRepoSlug);
      const { owner, repo } = this.parseRepo(githubRepoSlug);
      const inputsParams = inputs || {};
      const totalWorkflowRunsBefore = await this.listWorkflowRunsTotalCount(
        hostname,
        githubRepoSlug,
        workflowId
      );
      let totalWorkflowRunsAfter = totalWorkflowRunsBefore;
      const loadTime = 1500;
      const response = await octokit.actions.createWorkflowDispatch({
        owner,
        repo,
        workflow_id: workflowId,
        ref: branch,
        inputs: inputsParams
      });
      while (totalWorkflowRunsAfter === totalWorkflowRunsBefore) {
        await this.waitTime(loadTime);
        totalWorkflowRunsAfter = await this.listWorkflowRunsTotalCount(
          hostname,
          githubRepoSlug,
          workflowId
        );
      }
      return response.status;
    });
  }
  async stopWorkflowRun(hostname, githubRepoSlug, runId) {
    return this.catchOctokitError(async () => {
      const octokit = await this.getOctokit(hostname, githubRepoSlug);
      const { owner, repo } = this.parseRepo(githubRepoSlug);
      const response = await octokit.actions.cancelWorkflowRun({
        owner,
        repo,
        run_id: runId
      });
      return response.status;
    });
  }
  async listJobsForWorkflowRun(hostname, githubRepoSlug, id, pageSize, page) {
    return this.catchOctokitError(async () => {
      const octokit = await this.getOctokit(hostname, githubRepoSlug);
      const { owner, repo } = this.parseRepo(githubRepoSlug);
      const jobs = await octokit.actions.listJobsForWorkflowRun({
        owner,
        repo,
        run_id: id,
        per_page: pageSize,
        page
      });
      return jobs.data;
    });
  }
  async getWorkflowRunById(hostname, githubRepoSlug, runId) {
    return this.catchOctokitError(async () => {
      const octokit = await this.getOctokit(hostname, githubRepoSlug);
      const { owner, repo } = this.parseRepo(githubRepoSlug);
      const workflow = await octokit.actions.getWorkflowRun({
        owner,
        repo,
        run_id: runId
      });
      return workflow.data;
    });
  }
  async downloadJobLogsForWorkflowRun(hostname, githubRepoSlug, jobId) {
    return this.catchOctokitError(async () => {
      const octokit = await this.getOctokit(hostname, githubRepoSlug);
      const { owner, repo } = this.parseRepo(githubRepoSlug);
      const workflow = await octokit.actions.downloadJobLogsForWorkflowRun({
        owner,
        repo,
        job_id: jobId
      });
      return workflow.data;
    });
  }
  async getEnvironmentsList(hostname, githubRepoSlug) {
    return this.catchOctokitError(async () => {
      const octokit = await this.getOctokit(hostname, githubRepoSlug);
      const { owner, repo } = this.parseRepo(githubRepoSlug);
      const environments = await octokit.repos.getAllEnvironments({
        owner,
        repo
      });
      return environments.data;
    });
  }
  async waitTime(time) {
    return await new Promise((resolve) => setTimeout(resolve, time));
  }
  async getLatestWorkflowRun(hostname, workflowId, githubRepoSlug) {
    const octokit = await this.getOctokit(hostname, githubRepoSlug);
    const { owner, repo } = this.parseRepo(githubRepoSlug);
    const response = await octokit.actions.listWorkflowRuns({
      owner,
      repo,
      workflow_id: workflowId
    });
    return response.data.workflow_runs[0];
  }
  async getFileContentFromPath(hostname, githubRepoSlug, filePath, branch) {
    const octokit = await this.getOctokit(hostname, githubRepoSlug);
    const { owner, repo } = this.parseRepo(githubRepoSlug);
    const response = await octokit.repos.getContent({
      owner,
      repo,
      path: filePath,
      ref: branch
    });
    const data = response.data;
    const yamlContent = YAML__default.default.load(
      Buffer.from(data.content, "base64").toString("utf8")
    );
    return yamlContent;
  }
  async listWorkflowsDispatchParameters(hostname, githubRepoSlug, filePath, branch) {
    const dynamicWorkflow = filePath.startsWith("dynamic/");
    const yamlContent = dynamicWorkflow ? [] : await this.getFileContentFromPath(hostname, githubRepoSlug, filePath, branch);
    if (!yamlContent?.on?.workflow_dispatch?.inputs) return [];
    const inputs = yamlContent.on.workflow_dispatch.inputs;
    const mappedInputs = Object.keys(inputs).map(
      (input) => {
        const currentInput = inputs[input];
        const result = {
          name: input,
          description: currentInput.description ?? "",
          default: currentInput.default ?? "",
          required: currentInput.required ?? false,
          type: currentInput.type ?? "string"
        };
        if (currentInput.type === "choice") {
          result.options = currentInput.options;
        }
        return result;
      }
    );
    return mappedInputs;
  }
  async listWorkflowRunsTotalCount(hostname, githubRepoSlug, workflowId) {
    const octokit = await this.getOctokit(hostname, githubRepoSlug);
    const { owner, repo } = this.parseRepo(githubRepoSlug);
    const response = await octokit.actions.listWorkflowRuns({
      owner,
      repo,
      workflow_id: workflowId
    });
    return response.data.total_count;
  }
}

exports.GithubWorkflowsService = GithubWorkflowsService;
//# sourceMappingURL=GithubWorkflowsService.cjs.js.map
