import { determineValidHunks, fileDiffsToMap, isDiffContentsWellFormed } from "../diff_utils.js"
import { userNickname } from "../auth0-mgmt.js"
import { logger } from "../utils/logger.js"
import { getInstallationOctokitByOwner, isUserCollaborator } from "../github/github-utils.js"
import { githubApp } from "../github/github-app.js"
import {
  buildDependentPrTitle,
  buildPrCommentBody,
  generateOptimizationReviewTemplate,
  originalPRComment,
} from "../github/pr-changes-utils.js"
import {
  createDependentPullRequest,
  createNewBranchFromDiffContents,
} from "../github/create-pr-from-diffcontents.js"
import { posthog } from "../analytics.js"
import type { FileDiffContent } from "@codeflash-ai/code-suggester/build/src/types.js"
import { PrismaClient } from "@prisma/client"
import { prisma } from "@codeflash-ai/common"
import { sendSlackMessage } from "../github/slack_util.js"
import { Response } from "express"
import {
  requestApproval,
  requiresApprovalForRepo,
  isQualityMonitoringRepo,
  sendQualityMonitoringNotification,
} from "../github/optimization_approval.js"
import { registerRepositoryAndMember } from "./utils/github-repo-setup.js"
import { saveStagingReview } from "./create-staging.js"
import { AnyOctokit, AuthorizedUserReq, PullRequestDB } from "../types.js"
import { OptimizationReview } from "../OptimizationReview.js"
import {
  missingRequiredFields,
  validationFailure,
  unauthorized,
  githubInstallationError,
  githubNotCollaborator,
  optimizationRejected,
  unprocessableEntity,
  internalServerError,
} from "../exceptions/index.js"

// Dependencies interface for easier testing
export interface SuggestPrChangesDependencies {
  prisma: PrismaClient
  userNickname: typeof userNickname
  getInstallationOctokitByOwner: typeof getInstallationOctokitByOwner
  isUserCollaborator: typeof isUserCollaborator
  requiresApproval: typeof requiresApprovalForRepo
  requestApproval: typeof requestApproval
  posthog: typeof posthog
  githubApp: typeof githubApp
  isDiffContentsWellFormed: typeof isDiffContentsWellFormed
  fileDiffsToMap: typeof fileDiffsToMap
  determineValidHunks: typeof determineValidHunks
  buildDependentPrTitle: typeof buildDependentPrTitle
  buildPrCommentBody: typeof buildPrCommentBody
  createNewBranchFromDiffContents: typeof createNewBranchFromDiffContents
  createDependentPullRequest: typeof createDependentPullRequest
  sendSlackMessage: typeof sendSlackMessage
  updateOptimizationEvent: typeof updateOptimizationEvent
  saveStagingReview: typeof saveStagingReview
}

// Default dependencies
let dependencies: SuggestPrChangesDependencies = {
  prisma,
  userNickname,
  getInstallationOctokitByOwner,
  isUserCollaborator,
  requiresApproval: requiresApprovalForRepo,
  requestApproval,
  posthog,
  githubApp,
  isDiffContentsWellFormed,
  fileDiffsToMap,
  determineValidHunks,
  buildDependentPrTitle,
  buildPrCommentBody,
  createNewBranchFromDiffContents,
  createDependentPullRequest,
  sendSlackMessage,
  updateOptimizationEvent,
  saveStagingReview,
}

// For testing - allow dependency injection
export function setSuggestPrChangesDependencies(deps: Partial<SuggestPrChangesDependencies>) {
  dependencies = { ...dependencies, ...deps }
}

export function resetSuggestPrChangesDependencies() {
  dependencies = {
    prisma,
    userNickname,
    getInstallationOctokitByOwner,
    isUserCollaborator,
    requiresApproval: requiresApprovalForRepo,
    requestApproval,
    posthog,
    githubApp,
    isDiffContentsWellFormed,
    fileDiffsToMap,
    determineValidHunks,
    buildDependentPrTitle,
    buildPrCommentBody,
    createNewBranchFromDiffContents,
    createDependentPullRequest,
    sendSlackMessage,
    updateOptimizationEvent,
    saveStagingReview,
  }
}

const slackNotificationConfig = {
  "Future-House": ["aviary", "paper-qa"],
  "langflow-ai": ["langflow"],
  "albumentations-team": ["albumentations"],
  "Skyvern-AI": ["skyvern"],
  roboflow: ["inference"],
  gdsfactory: ["gdsfactory"],
  stackai: ["stackend"],
}

// Helper function to parse speedup values (same as create-staging.ts)
function parseSpeedupValue(value: unknown, suffix: "x" | "%"): number | null {
  if (value === undefined || value === null || value === "") {
    return null
  }

  let stringValue = String(value).trim().toLowerCase()

  if (stringValue.endsWith(suffix)) {
    stringValue = stringValue.slice(0, -1)
  }

  const parsed = parseFloat(stringValue.replace(",", ""))
  return isNaN(parsed) ? null : parsed
}

// Utility function to update optimization event in the database
export async function updateOptimizationEvent(
  traceId: string,
  prId?: string,
  prCommentFields?: any,
  originalLineProfiler?: string,
  optimizedLineProfiler?: string,
) {
  if (traceId !== "") {
    try {
      // Check existing data first (preserve staging data)
      const existing = await dependencies.prisma.optimization_events.findUnique({
        where: { trace_id: traceId },
        select: {
          function_name: true,
          speedup_x: true,
          file_path: true,
          speedup_pct: true,
          metadata: true,
        },
      })

      // Extract data from prCommentFields if provided
      const updateData: any = {
        ...(prId ? { pr_id: String(prId) } : {}),
        is_optimization_found: true,
        event_type: prId ? "pr_created" : "no-pr",
      }

      // Only add optimization data if prCommentFields provided AND not already set
      if (prCommentFields) {
        if (!existing?.function_name && prCommentFields.function_name) {
          updateData.function_name = prCommentFields.function_name
        }
        if (!existing?.file_path && prCommentFields.file_path) {
          updateData.file_path = prCommentFields.file_path
        }
        if (existing?.speedup_x == null && prCommentFields.speedup_x) {
          updateData.speedup_x = parseSpeedupValue(prCommentFields.speedup_x, "x")
        }
        if (existing?.speedup_pct == null && prCommentFields.speedup_pct) {
          updateData.speedup_pct = parseSpeedupValue(prCommentFields.speedup_pct, "%")
        }
      }

      // Add line profiler data to metadata if provided
      if (originalLineProfiler || optimizedLineProfiler) {
        const currentMetadata = (existing?.metadata ?? {}) as Record<string, unknown>
        if (originalLineProfiler && !currentMetadata.originalLineProfiler) {
          currentMetadata.originalLineProfiler = originalLineProfiler
        }
        if (optimizedLineProfiler && !currentMetadata.optimizedLineProfiler) {
          currentMetadata.optimizedLineProfiler = optimizedLineProfiler
        }
        updateData.metadata = currentMetadata
      }

      await dependencies.prisma.optimization_events.update({
        where: { trace_id: traceId },
        data: updateData,
      })
    } catch (eventError) {
      logger.error(
        "Failed to update optimization event:",
        { endpoint: "/cfapi/suggest-pr-changes", operation: "update_optimization_event" },
        {},
        eventError as Error,
      )
    }
  }
}

export async function suggestPrChanges(
  req: AuthorizedUserReq,
  res: Response,
): Promise<Response | undefined> {
  try {
    const {
      owner,
      repo,
      pullNumber,
      diffContents,
      prCommentFields,
      existingTests,
      generatedTests,
      coverage_message,
      replayTests,
      concolicTests,
      optimizationReview,
      originalLineProfiler,
      optimizedLineProfiler,
    } = req.body

    const userId = req.userId
    const traceId = req.body.traceId || ""
    logger.info(`traceId: ${traceId}`, req)

    if (!repo || !owner || !pullNumber || !dependencies.isDiffContentsWellFormed(diffContents)) {
      throw validationFailure("Missing or malformed fields: repo, owner, pullNumber, diffContents")
    }

    const nickname = await dependencies.userNickname(userId)
    if (nickname == null) {
      throw unauthorized("")
    }

    const installationOctokit = await dependencies.getInstallationOctokitByOwner(
      dependencies.githubApp,
      owner,
      repo,
      userId,
    )
    if (installationOctokit instanceof Error) {
      throw githubInstallationError(installationOctokit.message)
    }

    const isCollaborator = await dependencies.isUserCollaborator(
      installationOctokit,
      owner,
      repo,
      nickname,
    )
    if (!isCollaborator) {
      logger.info(`${nickname} is not a collaborator on ${owner}/${repo}`, req)
      throw githubNotCollaborator(`${owner}/${repo}`)
    }
    logger.info(`${nickname} is a collaborator on ${owner}/${repo}`, req)
    // TODO: Remove this background upsert logic after ensuring all old repositories have been saved.
    registerRepositoryAndMember(owner, repo, nickname, userId, installationOctokit)
      .then(() => {
        logger.info(`Background repo and member upsert completed for ${owner}/${repo}`, req)
      })
      .catch(err => {
        logger.errorWithSentry(
          `Error in background upsertRepoAndCreateMember`,
          req,
          { owner, repo, nickname, userId },
          err as Error,
        )
      })
    // Check if approval is required
    if (traceId && dependencies.requiresApproval(owner, repo)) {
      const optimization = await dependencies.prisma.optimization_features.findUnique({
        where: { trace_id: traceId },
        select: {
          approval_required: true,
          approval_status: true,
        },
      })

      if (optimization?.approval_status === "rejected") {
        throw optimizationRejected("This optimization request was rejected")
      }

      if (optimization?.approval_status === "approved") {
        logger.info(
          `Request ${traceId} was previously approved, continuing with PR suggestion`,
          req,
        )
        const result = await triggerSuggestPrChanges(
          owner,
          repo,
          pullNumber,
          diffContents,
          prCommentFields,
          existingTests,
          generatedTests,
          coverage_message,
          userId,
          nickname,
          installationOctokit,
          replayTests,
          concolicTests,
          traceId,
          optimizationReview,
          res,
          originalLineProfiler,
          optimizedLineProfiler,
        )

        if (result && typeof result === "object" && "status" in result) {
          return result
        }
        return res.json(result)
      } else {
        const requestData = {
          type: "suggest-pr-changes",
          owner,
          repo,
          pullNumber,
          diffContents,
          prCommentFields,
          existingTests,
          generatedTests,
          coverage_message,
          userId,
          optimizationReview,
          originalLineProfiler,
          optimizedLineProfiler,
        }

        await dependencies.requestApproval(
          traceId,
          owner,
          repo,
          prCommentFields.function_name,
          userId,
          requestData,
        )

        return res.status(202).json({
          status: "pending_approval",
          message:
            "This PR suggestion requires approval. You will be notified when it is processed.",
        })
      }
    }

    // No approval required, proceed with PR suggestion
    const result = await triggerSuggestPrChanges(
      owner,
      repo,
      pullNumber,
      diffContents,
      prCommentFields,
      existingTests,
      generatedTests,
      coverage_message,
      userId,
      nickname,
      installationOctokit,
      replayTests,
      concolicTests,
      traceId,
      optimizationReview,
      res,
      originalLineProfiler,
      optimizedLineProfiler,
    )

    // Check if this is a quality monitoring repo and send notification
    if (traceId && isQualityMonitoringRepo(owner, repo) && result && typeof result === "number") {
      const requestData = {
        type: "suggest-pr-changes",
        owner,
        repo,
        pullNumber,
        diffContents,
        prCommentFields,
        existingTests,
        generatedTests,
        coverage_message,
        userId,
        replayTests,
        concolicTests,
        optimizationReview,
        originalLineProfiler,
        optimizedLineProfiler,
      }

      // Send quality monitoring notification (non-blocking)
      sendQualityMonitoringNotification(
        traceId,
        owner,
        repo,
        prCommentFields.function_name,
        userId,
        requestData,
      ).catch(error => {
        logger.error(`Failed to send quality monitoring notification:`, req, {}, error as Error)
      })

      logger.info(
        `Quality monitoring notification triggered for ${owner}/${repo} PR #${pullNumber}`,
        req,
      )
    }

    // Don't call res.json(result) if result is already a Response object
    if (result && typeof result === "object" && "status" in result) {
      return result
    }
    return res.json(result)
  } catch (error: any) {
    // Try to fallback to staging if we have a traceId
    const traceId = req.body.traceId || ""
    if (traceId) {
      logger.info(`PR suggestion failed, falling back to staging for traceId: ${traceId}`, req)
      try {
        const stagingResult = await dependencies.saveStagingReview(
          req.body,
          req.userId,
          req.organizationId,
          req.subscriptionInfo,
        )
        if (stagingResult.status === 200) {
          return res.status(200).json({
            message: "PR suggestion failed, staging created as fallback",
            ...stagingResult.data,
          })
        }
        // Handle non-200 staging response - return staging's actual status/error
        logger.errorWithSentry(
          `Staging fallback returned status ${stagingResult.status}`,
          req,
          { reqBody: req.body, userId: req.userId, traceId, stagingResult },
          new Error(`Staging fallback returned status ${stagingResult.status}`),
        )
        return res.status(stagingResult.status).json({
          message: "PR suggestion failed and staging fallback also failed",
          ...stagingResult.data,
        })
      } catch (stagingError) {
        logger.errorWithSentry(
          `Staging fallback threw an exception`,
          req,
          { reqBody: req.body, userId: req.userId, traceId },
          stagingError as Error,
        )
        return res.status(500).json({
          message: "PR suggestion failed and staging fallback threw an error",
          error: stagingError instanceof Error ? stagingError.message : String(stagingError),
        })
      }
    }

    logger.errorWithSentry(
      `Error in /cfapi/suggest-pr-changes: ${error}`,
      req,
      { errorMessage: error.message },
      error as Error,
    )
    dependencies.posthog.capture({
      distinctId: req.userId,
      event: `cfapi-suggest-pr-changes-failed-error`,
      properties: { error: error.message, stack: error.stack },
    })
    throw internalServerError(`Error creating pull request: ${error.message}`)
  }
}

export async function triggerSuggestPrChanges(
  owner: string,
  repo: string,
  pullNumber: number,
  diffContents: any,
  prCommentFields: any,
  existingTests: string,
  generatedTests: string,
  coverage_message: string,
  userId: string,
  nickname: string,
  installationOctokit: any,
  replayTests: string = "",
  concolicTests: string = "",
  traceId: string = "",
  optimizationReview: string = "",
  res?: Response,
  originalLineProfiler: string = "",
  optimizedLineProfiler: string = "",
): Promise<Response | number | null> {
  try {
    const diffContentsMap: Map<string, FileDiffContent> = dependencies.fileDiffsToMap(diffContents)

    const { validHunks, invalidHunks } = await dependencies.determineValidHunks(
      installationOctokit.rest,
      { owner, repo },
      pullNumber,
      100,
      diffContentsMap,
    )

    const timestamp = new Date()
      .toISOString()
      .replace(/:/g, ".")
      .replace(/\.\d+Z$/, "")

    const newBranchName = `codeflash/optimize-pr${pullNumber}-${timestamp}`

    const originalPrData = await installationOctokit.rest.pulls.get({
      owner,
      repo,
      pull_number: pullNumber,
    })

    // Check if the PR is merged or closed - we can't suggest changes on merged/closed PRs
    if (originalPrData.data.merged) {
      logger.info(`PR #${pullNumber} is already merged, cannot suggest changes`, {
        endpoint: "/cfapi/suggest-pr-changes",
        operation: "pr_merged_check",
        owner,
        repo,
        userId,
      })
      throw unprocessableEntity(
        `Cannot suggest changes on merged PR #${pullNumber}. The PR was already merged.`,
      )
    }

    if (originalPrData.data.state === "closed") {
      logger.info(`PR #${pullNumber} is closed, cannot suggest changes`, {
        endpoint: "/cfapi/suggest-pr-changes",
        operation: "pr_closed_check",
        owner,
        repo,
        userId,
      })
      throw unprocessableEntity(
        `Cannot suggest changes on closed PR #${pullNumber}. The PR is no longer open.`,
      )
    }

    const baseBranch = originalPrData.data.head.ref
    logger.info(`Attempting to access ref for: ${owner}/${repo}, branch: ${baseBranch}`, {
      endpoint: "/cfapi/suggest-pr-changes",
      operation: "access_ref",
      owner,
      repo,
      userId,
    })

    const commitMessage = `Optimize ${prCommentFields.function_name} \n\n${prCommentFields.optimization_explanation}`

    let hasMultipleHunksInSameFile = false
    const hasMultipleFiles = validHunks.size > 1

    for (const [filePath, hunks] of validHunks.entries()) {
      if (hunks.length > 1) {
        logger.info(
          `File ${filePath} has ${hunks.length} hunks, using dependent PR instead of review comments`,
          {
            endpoint: "/cfapi/suggest-pr-changes",
            operation: "multiple_hunks",
            owner,
            repo,
            userId,
          },
        )
        hasMultipleHunksInSameFile = true
        break
      }
    }

    if (hasMultipleFiles) {
      logger.info(
        `Found ${validHunks.size} files with changes, using dependent PR instead of review comments`,
        { endpoint: "/cfapi/suggest-pr-changes", operation: "multiple_files", owner, repo, userId },
      )
    }

    if (
      invalidHunks.size > 0 ||
      validHunks.size > 1 ||
      hasMultipleFiles ||
      hasMultipleHunksInSameFile
    ) {
      logger.info(
        `Creating a dependent PR because there are ${invalidHunks.size > 0 ? "invalid hunks" : "multiple valid hunks"}.`,
        {
          userId,
          endpoint: "/cfapi/suggest-pr-changes",
          operation: "create_dependent_pr",
          owner,
          repo,
        },
      )
      logger.info(`Making a new dependent PR...`, {
        userId,
        endpoint: "/cfapi/suggest-pr-changes",
        operation: "create_dependent_pr",
        owner,
        repo,
      })
      if (
        OptimizationReview.MEDIUM === optimizationReview &&
        !dependencies.requiresApproval(owner, repo) &&
        traceId
      ) {
        const result = await dependencies.saveStagingReview(
          {
            owner,
            repo,
            pullNumber,
            baseBranch,
            prCommentFields,
            existingTests,
            generatedTests,
            coverage_message,
            replayTests,
            concolicTests,
            optimizationReview,
            traceId,
            diffContents,
          },
          userId,
          null, // organizationId - not available in this context
        )
        if (result.status == 200) {
          const commentBody: string = originalPRComment(
            prCommentFields,
            traceId, //We will send trace Id instead of PRNumber
            baseBranch,
            optimizationReview,
          )
          await installationOctokit.rest.issues.createComment({
            owner,
            repo,
            issue_number: pullNumber,
            body: commentBody,
          })
          if (res) {
            return res.status(200).json({ message: "staging created successfully" })
          }
        }
      }
      const branchCreated = await dependencies.createNewBranchFromDiffContents(
        installationOctokit,
        owner,
        repo,
        newBranchName,
        baseBranch,
        diffContentsMap,
        commitMessage,
      )

      if (!branchCreated) {
        throw new Error(`Failed to create branch ${newBranchName}`)
      }
      const newPrData = await dependencies.createDependentPullRequest(
        installationOctokit,
        owner,
        repo,
        pullNumber,
        newBranchName,
        baseBranch,
        prCommentFields,
        existingTests,
        generatedTests,
        coverage_message,
        replayTests,
        concolicTests,
        optimizationReview,
      )

      logger.info(
        `Created new dependent PR #${newPrData.data.number} from branch ${newPrData.data.head.ref}`,
        {
          userId,
          endpoint: "/cfapi/suggest-pr-changes",
          operation: "dependent_pr_created",
          owner,
          repo,
        },
      )

      if (slackNotificationConfig[owner as keyof typeof slackNotificationConfig]?.includes(repo)) {
        await dependencies.sendSlackMessage(
          `new dependent PR created: ${newPrData.data.html_url} for ${owner}/${repo}`,
        )
      }

      dependencies.posthog?.capture({
        distinctId: userId,
        event: `cfapi-suggest-pr-changes-success-dependent-pr-created`,
        properties: {
          owner,
          repo,
          newPrNumber: newPrData.data.number,
          newPrBranch: newPrData.data.head.ref,
          PRURL: newPrData.data.html_url,
        },
      })

      if (traceId !== "") {
        const pull_request_db = await dependencies.prisma.optimization_features.findUnique({
          where: {
            trace_id: traceId,
          },
          select: {
            pull_request: true,
          },
        })

        if (pull_request_db) {
          if (pull_request_db.pull_request === null || pull_request_db.pull_request === undefined) {
            pull_request_db.pull_request = {}
          }
          ;(pull_request_db as PullRequestDB).pull_request.dependent_pr_url =
            newPrData.data.html_url

          await dependencies.prisma.optimization_features.update({
            where: {
              trace_id: traceId,
            },
            data: {
              pull_request: pull_request_db.pull_request,
            },
          })
        }
      }

      await dependencies.updateOptimizationEvent(
        traceId,
        newPrData.data.id,
        prCommentFields,
        originalLineProfiler,
        optimizedLineProfiler,
      )

      if (res) {
        res.json(newPrData.data.number)
        return res
      }

      return newPrData.data.number
    } else {
      logger.info(`Creating unified review for a single valid hunk.`, {
        userId,
        endpoint: "/cfapi/suggest-pr-changes",
        operation: "create_unified_review",
        owner,
        repo,
      })

      const prCommentBody = dependencies.buildPrCommentBody(
        prCommentFields,
        existingTests,
        generatedTests,
        coverage_message,
        newBranchName,
        replayTests,
        concolicTests,
        traceId,
        { isUnifiedReview: true, includeHeader: false, isCollapsed: true },
      )
      let optReviewBadge = generateOptimizationReviewTemplate(optimizationReview)
      optReviewBadge &&= `\n\n${optReviewBadge}\n`
      const reviewComments = []
      let foundInvalidHunk = false

      for (const [filePath, hunks] of validHunks.entries()) {
        for (const hunk of hunks) {
          if (hunk.oldStart <= hunk.oldEnd) {
            const newContent = hunk.newContent.join("\n")
            const isLongDiff = newContent.length > 500
            let commentBody

            if (isLongDiff) {
              commentBody =
                `${prCommentBody}\n\n` +
                `<details>\n` +
                `<summary>Click to see suggested changes</summary>\n\n` +
                `\`\`\`suggestion\n${newContent}\n\`\`\`\n` +
                `</details>` +
                `\n${optReviewBadge}`
            } else {
              commentBody =
                `${prCommentBody}\n\n` +
                `\`\`\`suggestion\n${newContent}\n\`\`\`` +
                `\n${optReviewBadge}`
            }

            reviewComments.push({
              path: filePath,
              line: hunk.oldEnd,
              start_line: hunk.oldStart,
              side: "RIGHT",
              body: commentBody,
            })
          } else {
            logger.warn(
              `Found invalid review range for ${filePath}: start_line (${hunk.oldStart}) must be less than or equal to line (${hunk.oldEnd}) with content ${hunk.newContent.join("\n")}`,
              {
                userId,
                endpoint: "/cfapi/suggest-pr-changes",
                operation: "invalid_review_range",
                owner,
                repo,
              },
            )
            foundInvalidHunk = true
            break
          }
        }

        if (foundInvalidHunk) {
          break
        }
      }

      if (!foundInvalidHunk && reviewComments.length > 0) {
        const review = await installationOctokit.rest.pulls.createReview({
          owner,
          repo,
          pull_number: pullNumber,
          commit_id: originalPrData.data.head.sha,
          event: "COMMENT",
          comments: reviewComments,
        })

        logger.info(`Added review comment to PR #${pullNumber}: ${review.data.html_url}`, {
          userId,
          endpoint: "/cfapi/suggest-pr-changes",
          operation: "review_comment_added",
          owner,
          repo,
        })

        if (
          slackNotificationConfig[owner as keyof typeof slackNotificationConfig]?.includes(repo)
        ) {
          await dependencies.sendSlackMessage(
            `Suggestions made for ${review.data.html_url} in ${owner}/${repo}`,
          )
        }

        dependencies.posthog?.capture({
          distinctId: userId,
          event: `cfapi-suggest-pr-changes-success-suggestions-made`,
          properties: {
            owner,
            repo,
            reviewId: review.data.id,
            PRURL: review.data.html_url,
          },
        })

        if (traceId !== "") {
          const pull_request_db = await dependencies.prisma.optimization_features.findUnique({
            where: {
              trace_id: traceId,
            },
            select: {
              pull_request: true,
            },
          })

          if (pull_request_db) {
            if (
              pull_request_db.pull_request === null ||
              pull_request_db.pull_request === undefined
            ) {
              pull_request_db.pull_request = {}
            }

            ;(pull_request_db as PullRequestDB).pull_request.review_suggestion_pr_url =
              review.data.html_url

            await dependencies.prisma.optimization_features.update({
              where: {
                trace_id: traceId,
              },
              data: {
                pull_request: pull_request_db.pull_request,
              },
            })
          }
        }

        await dependencies.updateOptimizationEvent(
          traceId,
          undefined,
          prCommentFields,
          originalLineProfiler,
          optimizedLineProfiler,
        )

        if (res) {
          res.json(review.data.id)
          return res
        }

        return review.data.id
      } else {
        const reason = foundInvalidHunk
          ? "invalid line ordering in hunks"
          : "no valid review comments could be created"

        logger.warn(`Cannot create review due to ${reason}`, {
          userId,
          endpoint: "/cfapi/suggest-pr-changes",
          operation: "cannot_create_review",
          owner,
          repo,
        })

        logger.error(`Cannot create review comments due to ${reason}`, {
          userId,
          endpoint: "/cfapi/suggest-pr-changes",
          operation: "cannot_create_review",
          owner,
          repo,
        })
        throw unprocessableEntity(
          `Cannot create review comments due to ${reason}. Please consider creating a dependent PR instead`,
        )
      }
    }
  } catch (error: any) {
    // Re-throw AppExceptions to be handled by GlobalExceptionHandler
    if (error && typeof error === "object" && "getHttpStatus" in error) {
      throw error
    }

    logger.errorWithSentry(
      `Error in triggerSuggestPrChanges: ${error}`,
      {
        userId,
        endpoint: "/cfapi/suggest-pr-changes",
        operation: "trigger_suggest_pr_changes",
        owner,
        repo,
      },
      {
        errorMessage: error.message,
      },
      error as Error,
    )

    throw internalServerError(`Error creating pull request: ${error.message}`)
  }
}
