const { appCredentialsFromString, getAuthOptionsForRepo } = require('@electron/github-app-auth');

const { Octokit } = require('@octokit/rest');

const fs = require('node:fs');
const path = require('node:path');

const { PATCH_UP_APP_CREDS } = process.env;

const REPO_OWNER = 'electron';
const REPO_NAME = 'electron';

async function getAllPatchFiles(dir) {
  const files = [];

  async function walkDir(currentDir) {
    const entries = await fs.promises.readdir(currentDir, { withFileTypes: true });

    for (const entry of entries) {
      const fullPath = path.join(currentDir, entry.name);

      if (entry.isDirectory()) {
        await walkDir(fullPath);
      } else if (
        entry.isFile() &&
        (entry.name.endsWith('.patch') || entry.name === 'README.md' || entry.name === 'config.json')
      ) {
        const relativePath = path.relative(process.cwd(), fullPath);
        const content = await fs.promises.readFile(fullPath, 'utf8');
        files.push({
          path: relativePath,
          content
        });
      }
    }
  }

  await walkDir(dir);
  return files;
}

function getCurrentCommitSha() {
  return process.env.GITHUB_SHA;
}

function getCurrentBranch() {
  return process.env.GITHUB_HEAD_REF;
}

async function main() {
  if (!PATCH_UP_APP_CREDS) {
    throw new Error('PATCH_UP_APP_CREDS environment variable not set');
  }

  const currentBranch = getCurrentBranch();

  if (!currentBranch) {
    throw new Error('GITHUB_HEAD_REF environment variable not set. Patch Up only works in PR workflows currently.');
  }

  const octokit = new Octokit({
    ...(await getAuthOptionsForRepo(
      {
        name: REPO_OWNER,
        owner: REPO_NAME
      },
      appCredentialsFromString(PATCH_UP_APP_CREDS)
    ))
  });

  const patchesDir = path.join(process.cwd(), 'patches');

  // Get current git state
  const currentCommitSha = getCurrentCommitSha();

  // Get the tree SHA from the current commit
  const currentCommit = await octokit.git.getCommit({
    owner: REPO_OWNER,
    repo: REPO_NAME,
    commit_sha: currentCommitSha
  });
  const baseTreeSha = currentCommit.data.tree.sha;

  // Find all patch files
  const patchFiles = await getAllPatchFiles(patchesDir);

  if (patchFiles.length === 0) {
    throw new Error('No patch files found');
  }

  console.log(`Found ${patchFiles.length} patch files`);

  // Create a new tree with the patch files
  const tree = patchFiles.map((file) => ({
    path: file.path,
    mode: '100644',
    type: 'blob',
    content: file.content
  }));

  const treeResponse = await octokit.git.createTree({
    owner: REPO_OWNER,
    repo: REPO_NAME,
    base_tree: baseTreeSha,
    tree
  });

  // Create a new commit
  const commitMessage = 'chore: update patches';
  const commitResponse = await octokit.git.createCommit({
    owner: REPO_OWNER,
    repo: REPO_NAME,
    message: commitMessage,
    tree: treeResponse.data.sha,
    parents: [currentCommitSha]
  });

  // Update the branch reference
  await octokit.git.updateRef({
    owner: REPO_OWNER,
    repo: REPO_NAME,
    ref: `heads/${currentBranch}`,
    sha: commitResponse.data.sha
  });

  console.log(`Successfully pushed commit ${commitResponse.data.sha} to ${currentBranch}`);
}

if (require.main === module) {
  main().catch((err) => {
    console.error(err);
    process.exit(1);
  });
}
