GitFOSS
// 1st-party
import type { ReqHandler } from "@ethicdevs/react-monolith";
import { ResourceVisibility } from "@prisma/client";
// app
import type { RepositoryFileDiff } from "../../types";
import { AppRoute, AppRouteParams } from "../../routes.defs";
// app services
import { makeOrganizationService } from "../../services/organization";
import { makePullRequestService } from "../../services/pullRequest";
import { makeRepositoryService } from "../../services/repository";
import { makeUsersService } from "../../services/user";
// app views
// import RepositoryPullRequestsView, {
//   RepositoryPullRequestsViewProps,
// } from "../../views/repositoryPullRequests/RepositoryPullRequestsView";
// new merge service
import { buildRouteLink } from "../../utils/shared";

const postRepositoryPullRequestMergeAction: ReqHandler<
  AppRouteParams,
  AppRoute.REPOSITORY_PULL_REQUEST_MERGE_ACTION
> = async (request, reply) => {
  const { orgSlug, repoSlug, pullUid } = request.params;
  const { merge_message, merge_summary, delete_source_branch } = request.body;

  const shouldDeleteSourceBranch = delete_source_branch != null; // treat any presence as request to delete

  console.log("params:", {
    merge_message,
    merge_summary,
    delete_source_branch,
  });

  const orgService = makeOrganizationService({ request });
  const prService = makePullRequestService({ request });
  const repoService = makeRepositoryService({ request });
  const usersService = makeUsersService({ request });

  const currentUser =
    request.session.data.authenticated &&
    request.session.data.curr_user_uid != null
      ? await usersService.getUserById(request.session.data.curr_user_uid)
      : null;

  const pullRequest = await prService.getPullRequestByUid(
    orgSlug,
    repoSlug,
    pullUid,
  );

  if (pullRequest == null) {
    return reply.status(404).callNotFound();
  }

  const sourceRepo = await repoService.getRepositoryById(
    pullRequest.sourceRepositoryId,
  );

  if (sourceRepo == null) {
    return reply.status(404).callNotFound();
  }

  const targetRepo = await repoService.getRepositoryById(
    pullRequest.targetRepositoryId,
  );

  if (targetRepo == null) {
    return reply.status(404).callNotFound();
  }

  const sourceParentOrg = await orgService.getOrganizationById(
    sourceRepo.organizationId,
  );

  if (sourceParentOrg == null) {
    return reply.status(404).callNotFound();
  }

  if (sourceRepo.visibility === ResourceVisibility.PRIVATE) {
    if (currentUser == null) {
      return reply.status(404).callNotFound();
    } else if (
      (await repoService.canUserAccessRepository(currentUser, sourceRepo)) ===
      false
    ) {
      return reply.status(404).callNotFound();
    }
  }

  const targetParentOrg = await orgService.getOrganizationById(
    targetRepo.organizationId,
  );

  if (targetParentOrg == null) {
    return reply.status(404).callNotFound();
  }

  if (targetRepo.visibility === ResourceVisibility.PRIVATE) {
    if (currentUser == null) {
      return reply.status(404).callNotFound();
    } else if (
      (await repoService.canUserAccessRepository(currentUser, targetRepo)) ===
      false
    ) {
      return reply.status(404).callNotFound();
    }
  }

  // 1. Try add remote & compare pull request branches
  let fileDiffs = [] as RepositoryFileDiff[];

  if (sourceParentOrg.slug === targetParentOrg.slug) {
    fileDiffs = await repoService.getRepositoryRefDiff(
      sourceRepo,
      pullRequest.targetBranch,
      pullRequest.sourceBranch,
    );
  } else {
    fileDiffs = await repoService.getRepositoryRemoteRefDiff(
      sourceRepo,
      pullRequest.sourceBranch,
      targetRepo,
      pullRequest.targetBranch,
    );
  }

  // 2. Check if branches can be merged
  if (fileDiffs.length <= 0) {
    throw new Error("Cannot merge two branches without difference.");
  }

  // 3. Do the merge via service method
  try {
    const result = await prService.mergePullRequest({
      pullRequestId: pullRequest.id,
      mergeMessage: merge_message ?? undefined,
      deleteSourceBranch: shouldDeleteSourceBranch,
    });

    if (result.success !== true) {
      return reply.status(500).send({ error: "Merge failed" });
    }

    const updatedPull = result.updatedPullRequest || pullRequest;

    // Redirect to PR details page after merge
    return reply.redirect(
      303,
      buildRouteLink(AppRoute.REPOSITORY_PULL_REQUEST_DETAILS, {
        orgSlug,
        repoSlug,
        pullUid: updatedPull.uid,
      }),
    );
  } catch (err) {
    console.error("Merge action failed:", (err as Error).message);
    return reply
      .status(500)
      .send({ error: "Merge action failed", detail: (err as Error).message });
  }
};

export default postRepositoryPullRequestMergeAction;