GitFOSS
// 1st-party
import type { ReqHandler } from "@ethicdevs/react-monolith";
import { ResourceVisibility } from "@prisma/client";
// app
import type { RepositoryFileDiff } from "../../types";
import { AppRoute, AppRoutesParams } 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 RepositoryPullRequestDetailsView, {
  RepositoryPullRequestDetailsViewProps,
} from "../../views/repositoryPullRequests/RepositoryPullRequestDetailsView";

const getRepositoryPullRequestDetailsView: ReqHandler = async (
  request,
  reply
) => {
  const { orgSlug, repoSlug, pullUid } =
    request.params as AppRoutesParams[AppRoute.REPOSITORY_PULL_REQUEST_DETAILS]["params"];

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

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

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

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

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

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

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

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

  if (sourceParentOrg == null || targetParentOrg == 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();
    }
  }

  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();
    }
  }

  const commitLogs = await repoService.getRepositoryCommitLog(
    sourceRepo,
    "",
    pullRequest.sourceBranch,
    true
  );

  const lastCommit = commitLogs.length >= 1 ? commitLogs[0] : null;

  let filesDiffs = [] as RepositoryFileDiff[];

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

  const reqHandler = reply.makeRequestHandler(request, reply);
  return reqHandler<RepositoryPullRequestDetailsViewProps>(
    RepositoryPullRequestDetailsView.name,
    {
      filesDiffs,
      lastCommit,
      pullRequest,
      sourceParentOrg,
      sourceRepo,
      targetParentOrg,
      targetRepo,
      isCurrentUserAllowedToMerge: true,
    }
  );
};

export default getRepositoryPullRequestDetailsView;