import { fileDiffsToMap, isDiffContentsWellFormed } from "../diff_utils.js"
import type { FileDiffContent } from "@codeflash-ai/code-suggester/build/src/types.js"
import { userNickname } from "../auth0-mgmt.js"
import {
  addLabelToPullRequest,
  getInstallationOctokitByOwner,
  isBranchExists,
  isUserCollaborator,
} from "../github/github-utils.js"
import {
  buildBenchmarkInfo,
  buildOptimizationMetadata,
  buildPrTitle,
  buildResultDetails,
  buildResultFooter,
  buildResultHeader,
  buildResultTestReport,
  generateOptimizationReviewTemplate,
} from "../github/pr-changes-utils.js"
import { githubApp } from "../github/github-app.js"
import {
  assignReviewer,
  createNewBranchFromDiffContents,
  createNewPullRequest,
  PrCommentFields,
} from "../github/create-pr-from-diffcontents.js"
import { posthog } from "../analytics.js"
import { PrismaClient } from "@prisma/client"
import { Request, Response } from "express"
import {
  createAppInstallation,
  createRepositoryMember,
  getAppInstallationByInstalltionId,
  prisma,
  upsertRepository,
} from "@codeflash-ai/common"
import { AuthorizedUserReq } from "../types.js"
import {
  requestApproval,
  requiresApprovalForRepo,
  isQualityMonitoringRepo,
  sendQualityMonitoringNotification,
} from "../github/optimization_approval.js"
import { registerRepositoryAndMember } from "./utils/github-repo-setup.js"
import { logger } from "../utils/logger.js"
import { saveStagingReview } from "./create-staging.js"

// 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
}

// Helper function to add line profiler data to metadata (avoids duplication)
function addLineProfilerToMetadata(
  metadata: Record<string, unknown>,
  originalLineProfiler?: string,
  optimizedLineProfiler?: string,
): void {
  if (originalLineProfiler && !metadata.originalLineProfiler) {
    metadata.originalLineProfiler = originalLineProfiler
  }
  if (optimizedLineProfiler && !metadata.optimizedLineProfiler) {
    metadata.optimizedLineProfiler = optimizedLineProfiler
  }
}

// Define a comprehensive interface for PR title and body generation
export interface PrContentBuilder {
  buildResultHeader: typeof buildResultHeader
  buildBenchmarkInfo: typeof buildBenchmarkInfo
  buildResultDetails: typeof buildResultDetails
  buildResultTestReport: typeof buildResultTestReport
  buildResultFooter: typeof buildResultFooter
  buildPrTitle: typeof buildPrTitle
}

// Create a standalone PR title and body function with injectable dependencies
export function createStandalonePRTitleAndBody(
  prCommentFields: PrCommentFields,
  existingTests: string,
  generatedTests: string,
  coverage_message: string,
  newBranchName: string,
  builder: PrContentBuilder,
  replayTests: string = "",
  concolicTests: string = "",
  optimizationReview: string = "",
  trace_id: string,
  hasLineProfilerData: boolean = false,
): { title: string; body: string } {
  const prCommentHeader = builder.buildResultHeader(prCommentFields)

  const benchmarkInfo =
    prCommentFields.benchmark_details && prCommentFields.benchmark_details.length > 0
      ? builder.buildBenchmarkInfo(prCommentFields)
      : ""

  const prCommentBody = builder.buildResultDetails(prCommentFields)
  const prCommentTestReport = builder.buildResultTestReport(
    prCommentFields,
    existingTests,
    generatedTests,
    coverage_message,
    replayTests,
    concolicTests,
  )
  const prCommentFooter = builder.buildResultFooter(newBranchName)
  const title: string = builder.buildPrTitle(
    prCommentFields.function_name,
    prCommentFields.speedup_pct,
    prCommentFields.speedup_x,
  )

  const metadata = buildOptimizationMetadata(prCommentFields, trace_id)
  let optReviewBadge = generateOptimizationReviewTemplate(optimizationReview)
  optReviewBadge &&= ` ${optReviewBadge}\n`

  // Add line profiler link if profiler data exists
  let lineProfilerSection = ""
  if (hasLineProfilerData && trace_id) {
    lineProfilerSection = `\n### 📊 Performance Profile\n[View detailed line-by-line performance analysis](https://app.codeflash.ai/review-optimizations/${trace_id}/profiler)\n`
  }

  const body: string = benchmarkInfo
    ? `${metadata}\n${prCommentHeader}\n${benchmarkInfo}\n${prCommentBody}\n${prCommentTestReport}\n${lineProfilerSection}${prCommentFooter}${optReviewBadge}`
    : `${metadata}\n${prCommentHeader}\n${prCommentBody}\n${prCommentTestReport}\n${lineProfilerSection}${prCommentFooter}${optReviewBadge}`

  return { title, body }
}

// Create a wrapper function for createNewPullRequest to match test expectations
export async function createStandalonePullRequest(
  installationOctokit: any,
  owner: string,
  repo: string,
  title: string,
  body: string,
  newBranchName: string,
  baseBranch: string,
) {
  return await createNewPullRequest(
    installationOctokit,
    owner,
    repo,
    title,
    body,
    newBranchName,
    baseBranch,
  )
}

// Dependencies interface for easier testing
export interface CreatePrDependencies {
  prisma: PrismaClient
  userNickname: typeof userNickname
  getInstallationOctokitByOwner: typeof getInstallationOctokitByOwner
  isUserCollaborator: typeof isUserCollaborator
  requiresApproval: typeof requiresApprovalForRepo
  requestApproval: typeof requestApproval
  triggerCreatePr: typeof triggerCreatePr
  posthog: typeof posthog
  isDiffContentsWellFormed: typeof isDiffContentsWellFormed
  githubApp: typeof githubApp
  registerRepositoryAndMember: typeof registerRepositoryAndMember
}

// Dependencies for triggerCreatePr
export interface TriggerCreatePrDependencies {
  prisma: PrismaClient
  fileDiffsToMap: typeof fileDiffsToMap
  buildPrTitle: typeof buildPrTitle
  createNewBranchFromDiffContents: typeof createNewBranchFromDiffContents
  createStandalonePullRequest: typeof createStandalonePullRequest
  addLabelToPullRequest: typeof addLabelToPullRequest
  assignReviewer: typeof assignReviewer
  posthog: typeof posthog
  createStandalonePRTitleAndBody: typeof createStandalonePRTitleAndBody
  prContentBuilder: PrContentBuilder
}

// Default dependencies
const defaultPrContentBuilder: PrContentBuilder = {
  buildResultHeader,
  buildBenchmarkInfo,
  buildResultDetails,
  buildResultTestReport,
  buildResultFooter,
  buildPrTitle,
}

let dependencies: CreatePrDependencies = {
  prisma,
  userNickname,
  getInstallationOctokitByOwner,
  isUserCollaborator,
  requiresApproval: requiresApprovalForRepo,
  requestApproval,
  triggerCreatePr,
  posthog,
  isDiffContentsWellFormed,
  githubApp,
  registerRepositoryAndMember,
}

let triggerCreatePrDeps: TriggerCreatePrDependencies = {
  prisma,
  fileDiffsToMap,
  buildPrTitle,
  createNewBranchFromDiffContents,
  createStandalonePullRequest,
  addLabelToPullRequest,
  assignReviewer,
  posthog,
  createStandalonePRTitleAndBody,
  prContentBuilder: defaultPrContentBuilder,
}

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

export function resetCreatePrDependencies() {
  dependencies = {
    prisma,
    userNickname,
    getInstallationOctokitByOwner,
    isUserCollaborator,
    requiresApproval: requiresApprovalForRepo,
    requestApproval,
    triggerCreatePr,
    posthog,
    isDiffContentsWellFormed,
    githubApp,
    registerRepositoryAndMember,
  }
}

export function setTriggerCreatePrDependencies(deps: Partial<TriggerCreatePrDependencies>) {
  triggerCreatePrDeps = { ...triggerCreatePrDeps, ...deps }
}

export function resetTriggerCreatePrDependencies() {
  triggerCreatePrDeps = {
    prisma,
    fileDiffsToMap,
    buildPrTitle,
    createNewBranchFromDiffContents,
    createStandalonePullRequest,
    addLabelToPullRequest,
    assignReviewer,
    posthog,
    createStandalonePRTitleAndBody,
    prContentBuilder: defaultPrContentBuilder,
  }
}

export async function createPr(req: Request, res: Response) {
  try {
    const {
      owner,
      repo,
      baseBranch,
      diffContents,
      prCommentFields,
      existingTests,
      generatedTests,
      coverage_message,
      replayTests,
      concolicTests,
      optimizationReview,
      stagingBranch,
      originalLineProfiler,
      optimizedLineProfiler,
    } = req.body

    // Get source from headers instead of body
    const source = (req.headers?.["x-codeflash-source"] as string) || "unknown"
    const isCfWebApp = source === "webapp"
    const userId = (req as any).userId
    const organizationId = (req as any).organizationId || null
    const traceId = req.body.traceId || ""
    const userTier = (req as any).subscriptionInfo?.tier || "free"

    // If stagingBranch is provided, we don't need diffContents
    const needsDiffContents = !stagingBranch
    if (
      !repo ||
      !owner ||
      !baseBranch ||
      (needsDiffContents && !dependencies.isDiffContentsWellFormed(diffContents))
    ) {
      res.status(400).send("Missing or malformed fields")
      return
    }

    // Fetch user nickname and installation octokit in parallel (independent calls)
    const [nickname, installationOctokit] = await Promise.all([
      dependencies.userNickname(userId),
      dependencies.getInstallationOctokitByOwner(dependencies.githubApp, owner, repo, userId),
    ])

    if (nickname == null) {
      res.status(401).json({ error: "Unauthorized" })
      return
    }

    if (installationOctokit instanceof Error) {
      res.status(401).json({ error: installationOctokit.message })
      return
    }
    const isCollaborator = await dependencies.isUserCollaborator(
      installationOctokit,
      owner,
      repo,
      nickname,
    )

    if (!isCollaborator) {
      const errorMsg = `You are not a collaborator on ${owner}/${repo}. Please ensure you have write access to the repository.`
      logger.info(`${nickname} is not a collaborator on ${owner}/${repo}`, req, {
        nickname,
        traceId,
        repo: `${owner}/${repo}`,
      })
      return res.status(403).json({ error: errorMsg })
    }
    if (
      isCfWebApp &&
      !(await isBranchExists({ owner, repo, branch: baseBranch, installationOctokit }))
    ) {
      res.status(404).json({
        error: `The base branch "${baseBranch}" does not exist in ${owner}/${repo}. Please verify the branch name and try again.`,
      })
      return
    }
    logger.info(`Verified ${nickname} is a collaborator on ${owner}/${repo}`, req)
    // TODO: Remove this background upsert logic after ensuring all old repositories have been saved.
    dependencies
      .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,
        )
      })

    if (traceId && dependencies.requiresApproval(owner, repo) && !isCfWebApp) {
      const optimization = await dependencies.prisma.optimization_features.findUnique({
        where: { trace_id: traceId },
        select: {
          approval_required: true,
          approval_status: true,
        },
      })

      if (optimization?.approval_status === "rejected") {
        return res.status(403).json({
          status: "rejected",
          message: "This optimization request was rejected.",
        })
      }

      if (optimization?.approval_status === "approved") {
        logger.info(`Request ${traceId} was previously approved, continuing with PR creation`, req)
        const prNumber = await dependencies.triggerCreatePr(
          owner,
          repo,
          baseBranch,
          diffContents,
          prCommentFields,
          existingTests,
          generatedTests,
          coverage_message,
          userId,
          nickname,
          installationOctokit,
          replayTests,
          concolicTests,
          traceId,
          optimizationReview,
          stagingBranch,
          userTier,
          organizationId,
          originalLineProfiler,
          optimizedLineProfiler,
        )

        if (prNumber > 0) {
          return res.json(prNumber)
        } else {
          return res.status(500).send("Error creating pull request")
        }
      } else {
        const requestData = {
          type: "create-pr",
          owner,
          repo,
          baseBranch,
          diffContents,
          prCommentFields,
          existingTests,
          generatedTests,
          coverage_message,
          userId,
          replayTests,
          concolicTests,
        }

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

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

    const prNumber = await dependencies.triggerCreatePr(
      owner,
      repo,
      baseBranch,
      diffContents,
      prCommentFields,
      existingTests,
      generatedTests,
      coverage_message,
      userId,
      nickname,
      installationOctokit,
      replayTests,
      concolicTests,
      traceId,
      optimizationReview,
      stagingBranch,
      userTier,
      organizationId,
      originalLineProfiler,
      optimizedLineProfiler,
    )

    if (prNumber > 0) {
      // Check if this is a quality monitoring repo and send notification
      if (traceId && isQualityMonitoringRepo(owner, repo)) {
        const requestData = {
          type: "create-pr",
          owner,
          repo,
          baseBranch,
          diffContents,
          prCommentFields,
          existingTests,
          generatedTests,
          coverage_message,
          userId,
        }

        const prUrl = `https://github.com/${owner}/${repo}/pull/${prNumber}`

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

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

      return res.json(prNumber)
    } else {
      // If PR creation failed, fallback to staging
      if (traceId) {
        logger.info(`PR creation failed, falling back to staging for traceId: ${traceId}`, req)
        try {
          const stagingResult = await saveStagingReview(
            req.body,
            userId,
            organizationId,
            (req as any).subscriptionInfo,
          )
          if (stagingResult.status === 200) {
            return res.status(200).json({
              message: "PR creation 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, traceId, stagingResult },
            new Error(`Staging fallback returned status ${stagingResult.status}`),
          )
          return res.status(stagingResult.status).json({
            message: "PR creation failed and staging fallback also failed",
            ...stagingResult.data,
          })
        } catch (stagingError) {
          logger.errorWithSentry(
            `Staging fallback threw an exception:`,
            req,
            { reqBody: req.body, userId, traceId },
            stagingError as Error,
          )
          return res.status(500).json({
            message: "PR creation failed and staging fallback threw an error",
            error: stagingError instanceof Error ? stagingError.message : String(stagingError),
          })
        }
      }
      return res.status(500).send("Error creating pull request")
    }
  } catch (error) {
    logger.errorWithSentry(
      `Error in /cfapi/create-pr`,
      req,
      {
        errorMessage: error instanceof Error ? error.message : String(error),
        reqBody: req.body,
        userId: (req as any).userId,
      },
      error as Error,
    )
    if (error instanceof Error) {
      // Capture error in PostHog
      dependencies.posthog?.capture({
        distinctId: (req as any).userId,
        event: `cfapi-create-pr-failed-error-creating-standalone-pr`,
        properties: {
          error: error.message,
        },
      })
      res.status(500).send(`Error creating pull request: ${error.message}`)
    } else {
      res.status(500).send(`Error creating pull request`)
    }
  }
}

export async function triggerCreatePr(
  owner,
  repo,
  baseBranch,
  diffContents,
  prCommentFields,
  existingTests,
  generatedTests,
  coverage_message,
  userId,
  nickname,
  installationOctokit,
  replayTests = "",
  concolicTests = "",
  traceId = "",
  optimizationReview = "",
  stagingBranch = "",
  userTier = "free",
  organizationId: string | null = null,
  originalLineProfiler: string | undefined = undefined,
  optimizedLineProfiler: string | undefined = undefined,
): Promise<number> {
  const isPaidUser = userTier.toLowerCase() !== "free"
  logger.info(
    `[triggerCreatePr] Starting PR creation for ${owner}/${repo}`,
    {
      traceId,
      userId,
      endpoint: "/cfapi/create-pr",
      operation: "trigger_create_pr",
      owner,
      repo,
    },
    {
      traceId,
      nickname,
      baseBranch,
      functionName: prCommentFields?.function_name,
      stagingBranch: stagingBranch || "none (will create new branch)",
      userTier,
      isPaidUser,
    },
  )

  try {
    let newBranchName: string

    // If staging branch is provided, use it directly instead of creating a new branch
    if (stagingBranch) {
      newBranchName = stagingBranch
      console.log(`[triggerCreatePr] Using existing staging branch: ${newBranchName}`)
    } else {
      const diffContentsMap: Map<string, FileDiffContent> =
        triggerCreatePrDeps.fileDiffsToMap(diffContents)

      const timestamp = Date.now()
      const encodedTimestamp = timestamp.toString(36)

      newBranchName = `codeflash/optimize-${prCommentFields.function_name}-${encodedTimestamp}`
      const commitMessage = `Optimize ${prCommentFields.function_name}\n\n${prCommentFields.optimization_explanation}`

      logger.info(`Creating branch: ${newBranchName}`, {
        userId,
        endpoint: "/cfapi/create-pr",
        operation: "create_branch",
        owner,
        repo,
      })
      const branchCreated = await triggerCreatePrDeps.createNewBranchFromDiffContents(
        installationOctokit,
        owner,
        repo,
        newBranchName,
        baseBranch,
        diffContentsMap,
        commitMessage,
      )

      if (!branchCreated) {
        console.error(`[triggerCreatePr] Failed to create branch ${newBranchName}`)
        throw new Error(`Failed to create branch ${newBranchName}`)
      }
      console.log(`[triggerCreatePr] Branch created successfully: ${newBranchName}`)
    }

    // Use the injectable function instead of the hardcoded one
    const hasLineProfilerData = !!(originalLineProfiler || optimizedLineProfiler)
    const { title, body } = triggerCreatePrDeps.createStandalonePRTitleAndBody(
      prCommentFields,
      existingTests,
      generatedTests,
      coverage_message,
      newBranchName,
      triggerCreatePrDeps.prContentBuilder,
      replayTests,
      concolicTests,
      optimizationReview,
      traceId,
      hasLineProfilerData,
    )

    logger.info(`Creating PR with title: ${title}`, {
      userId,
      endpoint: "/cfapi/create-pr",
      operation: "create_pr",
      owner,
      repo,
    })
    const newPrData = await triggerCreatePrDeps.createStandalonePullRequest(
      installationOctokit,
      owner,
      repo,
      title,
      body,
      newBranchName,
      baseBranch,
    )
    logger.info(`PR created successfully: #${newPrData.data.number}`, {
      userId,
      endpoint: "/cfapi/create-pr",
      operation: "create_pr",
      owner,
      repo,
    })
    // Run post-PR-creation tasks in parallel:
    // 1. DB optimization_events update (non-fatal errors caught internally)
    // 2. GitHub API calls: assign reviewer + add labels (run in parallel with each other)
    // 3. DB optimization_features update
    const updateOptimizationEventsTask = (async () => {
      try {
        // Check existing data first (preserve staging data)
        const existing = await triggerCreatePrDeps.prisma.optimization_events.findUnique({
          where: { trace_id: traceId },
          select: {
            function_name: true,
            speedup_x: true,
            file_path: true,
            speedup_pct: true,
            staging_storage_type: true,
            metadata: true,
          },
        })

        const updateData: any = {
          pr_id: String(newPrData.data.id),
          pr_url: `https://github.com/${owner}/${repo}/pull/${newPrData.data.number}`,
          is_optimization_found: true,
          event_type: "pr_created",
        }

        // Check if we should clean up plain text data (user is paid OR org has subscription)
        let shouldCleanupData = isPaidUser
        if (!shouldCleanupData && organizationId && traceId) {
          // Check if org has subscription
          const org = await triggerCreatePrDeps.prisma.organizations.findUnique({
            where: { id: organizationId },
            select: { subscription: true },
          })
          if (org?.subscription) {
            shouldCleanupData = true
            console.log(
              `[triggerCreatePr] Org has subscription - will cleanup plain text data for traceId: ${traceId}`,
            )
          }
        }

        // If user is paid or org has subscription, convert to git_branch storage and clear diffContents
        if (shouldCleanupData && traceId) {
          if (existing) {
            const currentMetadata = (existing.metadata ?? {}) as Record<string, unknown>

            // Remove diffContents from metadata if it exists (plain_text mode stores it there)
            if (currentMetadata.diffContents) {
              delete currentMetadata.diffContents
            }

            // Update metadata with the new staging branch name
            currentMetadata.staging_branch_name = newBranchName
            currentMetadata.storageType = "git_branch"

            // Add line profiler data if provided and not already present
            addLineProfilerToMetadata(currentMetadata, originalLineProfiler, optimizedLineProfiler)

            updateData.staging_storage_type = "git_branch"
            updateData.metadata = currentMetadata
            updateData.is_staging = true
            console.log(
              `[triggerCreatePr] Paid user/subscribed org: Converting storage to git_branch for traceId: ${traceId}`,
            )
          }
        } else if (traceId && (originalLineProfiler || optimizedLineProfiler)) {
          // For non-paid users, still add line profiler data if provided
          const currentMetadata = (existing?.metadata ?? {}) as Record<string, unknown>
          addLineProfilerToMetadata(currentMetadata, originalLineProfiler, optimizedLineProfiler)
          updateData.metadata = currentMetadata
        }
        // Only add if missing (preserve staging data)
        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, "%")
          }
        }

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

    // Run reviewer assignment and label additions in parallel
    const githubPostPrTasks: Array<Promise<void>> = [
      triggerCreatePrDeps.assignReviewer(
        installationOctokit,
        owner,
        repo,
        newPrData.data.number,
        nickname,
      ),
      triggerCreatePrDeps.addLabelToPullRequest(
        installationOctokit,
        owner,
        repo,
        newPrData.data.number,
      ),
    ]
    if (optimizationReview) {
      githubPostPrTasks.push(
        triggerCreatePrDeps.addLabelToPullRequest(
          installationOctokit,
          owner,
          repo,
          newPrData.data.number,
          `🎯 Quality: ${optimizationReview.charAt(0).toUpperCase() + optimizationReview.slice(1).toLowerCase()}`,
          "FFC043",
          "Optimization Quality according to Codeflash",
        ),
      )
    }

    const updateOptimizationFeaturesTask = (async () => {
      if (traceId !== "") {
        const pull_request_db = await triggerCreatePrDeps.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.pull_request as any).new_pr_url = newPrData.data.html_url

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

    // Wait for all post-PR tasks in parallel
    await Promise.all([
      updateOptimizationEventsTask,
      Promise.all(githubPostPrTasks),
      updateOptimizationFeaturesTask,
    ])

    logger.info(`Created new PR #${newPrData.data.number} with branch ${newPrData.data.head.ref}`, {
      userId,
      endpoint: "/cfapi/create-pr",
      operation: "pr_created",
      owner,
      repo,
    })
    triggerCreatePrDeps.posthog?.capture({
      distinctId: userId,
      event: `cfapi-create-pr-success-standalone-pr-created`,
      properties: {
        owner,
        repo,
        newPrNumber: newPrData.data.number,
        newPrBranch: newPrData.data.head.ref,
        PRURL: newPrData.data.html_url,
      },
    })

    return newPrData.data.number
  } catch (error) {
    logger.errorWithSentry(
      `Error creating PR for ${owner}/${repo}`,
      {
        userId,
        endpoint: "/cfapi/create-pr",
        operation: "trigger_create_pr",
        owner,
        repo,
        traceId,
      },
      {
        nickname,
        baseBranch,
        functionName: prCommentFields?.function_name,
        errorMessage: error instanceof Error ? error.message : String(error),
      },
      error as Error,
    )
    return -1
  }
}

// Endpoint to manually add repositories, similar to webhook logic
export async function addRepositoryManually(req: AuthorizedUserReq, res: Response): Promise<void> {
  try {
    const { repositories_added, installation } = req.body

    if (!repositories_added || !installation?.id) {
      res.status(400).send("Missing required fields")
      return
    }

    const installationExists = await getAppInstallationByInstalltionId(installation.id)

    if (!installationExists) {
      await createAppInstallation({
        installation_id: installation.id,
        account_id: installation.account.id,
        account_login: installation.account.login,
        account_type: installation.account.type,
      })
      logger.info(`Installation created for ID: ${installation.id}`, req)
    }

    for (const repo of repositories_added) {
      try {
        await prisma.repositories.upsert({
          where: { github_repo_id: String(repo.id) },
          update: {
            name: repo.name,
            full_name: repo.full_name,
            is_private: repo.private,
            installation_id: installation.id,
          },
          create: {
            github_repo_id: String(repo.id),
            installation_id: installation.id,
            name: repo.name,
            full_name: repo.full_name,
            is_private: repo.private,
            is_active: true,
          },
        })
        const savedRepo = await upsertRepository({
          github_repo_id: String(repo.id),
          installation_id: installation.id,
          name: repo.name,
          full_name: repo.full_name,
          is_private: repo.private,
        })
        await createRepositoryMember({
          repository_id: savedRepo.id,
          user_id: req.userId,
          role: "",
        })
        logger.info(`Repository upserted: ${repo.full_name}`, req)
      } catch (error) {
        logger.errorWithSentry(
          `Failed to add/reactivate repository ${repo.full_name}`,
          req,
          { repoId: repo.id, repoFullName: repo.full_name },
          error as Error,
        )
      }
    }

    res.status(200).send("Repositories added successfully")
  } catch (error) {
    logger.errorWithSentry(`Error adding repositories:`, req, {}, error as Error)
    if (error instanceof Error) {
      res.status(500).send(`Error adding repositories: ${error.message}`)
    } else {
      res.status(500).send("Error adding repositories")
    }
  }
}
