"use server"

import { CF_API } from "@/app/api/const"
import { ActionResponse, createErrorResponse, createSuccessResponse } from "@/lib/action-response"
import { getRepositoriesForAccountCached } from "@/lib/services/repository-utils"
import { auth0 } from "@/lib/auth0"
import { AccountPayload, buildOptimizationOrCondition, prisma } from "@codeflash-ai/common"
import * as Sentry from "@sentry/nextjs"
import { trackOptimizationReviewed } from "@/lib/analytics/tracking"
import { getActionAccountContext } from "@/lib/server/get-account-context"

export interface DiffContent {
  oldContent: string
  newContent: string
}

export interface StagingCodeResponse {
  storageType: "git_branch"
  diffContents: Record<string, DiffContent>
  stagingBranchName: string
  baseBranch: string | null
  functionName: string | null
  filePath: string | null
}

export interface GetStagingCodeParams {
  stagingBranchName: string
  baseBranch: string
  fullRepoName: string
  installationId: number
  functionName?: string
  filePath?: string
}

async function findAuthorizedOptimizationEvent(
  payload: AccountPayload,
  identifiers: { id?: string; trace_id?: string },
  queryOptions: Record<string, unknown> = {},
) {
  const repoIds = (await getRepositoriesForAccountCached(payload)).repoIds
  const where = {
    ...identifiers,
    ...buildOptimizationOrCondition(payload, repoIds),
  }

  return prisma.optimization_events.findFirst({
    where,
    ...queryOptions,
  } as any)
}

async function getAuthorizedActionContext() {
  return getActionAccountContext()
}

async function getAuthorizedEventById(eventId: string, queryOptions: Record<string, unknown> = {}) {
  const accountContext = await getAuthorizedActionContext()
  if (!accountContext) {
    return null
  }

  const event = await findAuthorizedOptimizationEvent(
    accountContext.payload,
    { id: eventId },
    queryOptions,
  )

  return {
    accountContext,
    event,
  }
}

async function getAuthorizedEventByTraceId(
  traceId: string,
  queryOptions: Record<string, unknown> = {},
) {
  const accountContext = await getAuthorizedActionContext()
  if (!accountContext) {
    return null
  }

  const event = await findAuthorizedOptimizationEvent(
    accountContext.payload,
    { trace_id: traceId },
    queryOptions,
  )

  return {
    accountContext,
    event,
  }
}

async function getStagingCodeFromApi(
  params: GetStagingCodeParams,
): Promise<ActionResponse<StagingCodeResponse>> {
  const cfapiUrl = process.env.CODEFLASH_CFAPI_URL
  const session = await auth0.getAccessToken()

  if (!cfapiUrl || !session?.token) {
    return createErrorResponse("Please sign in to continue")
  }

  try {
    const response = await fetch(`${cfapiUrl}/${CF_API}/get-staging-code`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Authorization: `Bearer ${session.token}`,
        "X-CodeFlash-Source": "webapp",
      },
      body: JSON.stringify(params),
    })

    if (!response.ok) {
      const contentType = response.headers.get("content-type")
      let errorMessage = "Failed to fetch staging code"

      if (contentType?.includes("json")) {
        const error = await response.json()
        errorMessage = error.error || error.message || errorMessage
      } else {
        const text = await response.text()
        if (text) errorMessage = text
      }

      if (response.status === 403) {
        return createErrorResponse("You are not a collaborator on this repository")
      }
      if (response.status === 404) {
        return createErrorResponse("Staging code not found")
      }

      return createErrorResponse(errorMessage)
    }

    const data = await response.json()
    return createSuccessResponse(data as StagingCodeResponse)
  } catch (error: any) {
    console.error("[getStagingCodeFromApi] Error:", error)
    Sentry.captureException(error)
    return createErrorResponse(error?.message || "Failed to fetch staging code")
  }
}

export interface CommitStagingCodeResponse {
  message: string
  commitSha: string
  stagingBranchName: string
}

export async function commitStagingCode(
  traceId: string,
  fileChanges: Record<string, string>,
  commitMessage?: string,
): Promise<ActionResponse<CommitStagingCodeResponse>> {
  const authorizedEvent = await getAuthorizedEventByTraceId(traceId, {
    select: { id: true },
  })
  if (!authorizedEvent) {
    return createErrorResponse("Unauthorized")
  }
  if (!authorizedEvent.event) {
    return createErrorResponse("Optimization event not found")
  }

  const cfapiUrl = process.env.CODEFLASH_CFAPI_URL
  const session = await auth0.getAccessToken()

  if (!cfapiUrl || !session?.token) {
    return createErrorResponse("Please sign in to continue")
  }

  try {
    const response = await fetch(`${cfapiUrl}/${CF_API}/commit-staging-code`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Authorization: `Bearer ${session.token}`,
        "X-CodeFlash-Source": "webapp",
      },
      body: JSON.stringify({
        traceId,
        fileChanges,
        commitMessage,
      }),
    })

    if (!response.ok) {
      const contentType = response.headers.get("content-type")
      let errorMessage = "Failed to commit changes"

      if (contentType?.includes("json")) {
        const error = await response.json()
        errorMessage = error.error || error.message || errorMessage
      } else {
        const text = await response.text()
        if (text) errorMessage = text
      }

      if (response.status === 403) {
        return createErrorResponse("You are not a collaborator on this repository")
      }
      if (response.status === 404) {
        return createErrorResponse("Staging branch not found")
      }

      return createErrorResponse(errorMessage)
    }

    const data = await response.json()
    return createSuccessResponse(data as CommitStagingCodeResponse)
  } catch (error: any) {
    console.error("[commitStagingCode] Error:", error)
    Sentry.captureException(error)
    return createErrorResponse(error?.message || "Failed to commit changes")
  }
}

async function getOptimizationEventById({
  payload,
  trace_id,
}: {
  payload: AccountPayload
  trace_id: string
}) {
  const repoIds = (await getRepositoriesForAccountCached(payload)).repoIds
  const where: any = {
    trace_id,
    ...buildOptimizationOrCondition(payload, repoIds),
  }

  // Fire both queries in parallel — features only needs trace_id, not the event result
  const [event, features] = await Promise.all([
    prisma.optimization_events.findFirst({
      where,
      include: {
        repository: {
          select: { id: true, full_name: true, name: true, installation_id: true },
        },
      },
    }),
    prisma.optimization_features.findUnique({
      where: { trace_id },
      select: {
        review_quality: true,
        review_explanation: true,
      },
    }),
  ])

  if (!event) {
    return null
  }

  // Track that this optimization was reviewed
  const userId = "userId" in payload ? payload.userId : undefined
  if (userId) {
    trackOptimizationReviewed(userId, {
      traceId: event.trace_id,
      functionName: event.function_name,
      repositoryName: event.repository?.full_name ?? null,
      status: event.status,
    })
  }

  return {
    ...event,
    review_quality: features?.review_quality || null,
    review_explanation: features?.review_explanation || null,
  }
}
export async function saveOptimizationChanges({
  eventId,
  filePath,
  newContent,
}: {
  eventId: string
  filePath: string
  newContent: string
}) {
  const authorizedEvent = await getAuthorizedEventById(eventId, {
    select: { id: true, metadata: true },
  })
  if (!authorizedEvent) {
    return {
      success: false,
      error: "Unauthorized",
    }
  }
  if (!authorizedEvent.event) {
    return {
      success: false,
      error: "Event not found",
    }
  }

  try {
    // Get the current metadata
    const currentMetadata = (authorizedEvent.event.metadata as any) || {}
    const currentDiffContents = currentMetadata.diffContents || {}

    // Update only the specific file's content
    const updatedDiffContents = {
      ...currentDiffContents,
      [filePath]: {
        ...currentDiffContents[filePath],
        newContent: newContent,
      },
    }

    // Update the metadata with new diff contents
    const updatedMetadata = {
      ...currentMetadata,
      diffContents: updatedDiffContents,
      lastModified: new Date().toISOString(),
    }

    // Save to database
    const updatedEvent = await prisma.optimization_events.update({
      where: {
        id: eventId,
      },
      data: {
        metadata: updatedMetadata,
      },
    })

    return { success: true, event: updatedEvent }
  } catch (error) {
    console.error("Error saving optimization changes:", error)
    return {
      success: false,
      error: error instanceof Error ? error.message : "Failed to save changes",
    }
  }
}

export async function createPullRequest({
  traceId,
  diffContents,
  prCommentFields,
  generatedTests,
  existingTests,
  functionName,
  filePath,
  speedupX,
  speedupPct,
  baseBranch,
  full_repo_name,
  coverage_message,
  originalLineProfiler,
  optimizedLineProfiler,
}: {
  traceId: string
  diffContents: Record<string, { oldContent: string; newContent: string }>
  prCommentFields?: any
  generatedTests?: string
  existingTests?: string
  functionName?: string
  filePath?: string
  speedupX?: string
  speedupPct?: string
  baseBranch?: string
  full_repo_name?: string
  coverage_message?: string
  originalLineProfiler?: string
  optimizedLineProfiler?: string
}): Promise<ActionResponse> {
  const authorizedEvent = await getAuthorizedEventByTraceId(traceId, {
    include: {
      repository: {
        select: { full_name: true },
      },
    },
  })
  if (!authorizedEvent) {
    return createErrorResponse("Unauthorized")
  }
  if (!authorizedEvent.event) {
    return createErrorResponse("Optimization event not found")
  }
  const authorizedRepository = (
    authorizedEvent.event as { repository?: { full_name?: string | null } | null }
  ).repository
  if (
    full_repo_name &&
    authorizedRepository?.full_name &&
    authorizedRepository.full_name !== full_repo_name
  if (full_repo_name && !authorizedRepository?.full_name) {
    return createErrorResponse("Repository not found for this optimization event")
  }
  if (
    full_repo_name &&
    authorizedRepository?.full_name &&
    authorizedRepository.full_name !== full_repo_name
  ) {
    return createErrorResponse("Repository mismatch for optimization event")
  }
    return createErrorResponse("Repository mismatch for optimization event")
  }

  const cfapiUrl = process.env.CODEFLASH_CFAPI_URL
  const session = await auth0.getAccessToken()

  if (!cfapiUrl || !session?.token) {
    return createErrorResponse("Please sign in to continue")
  }

  const [owner, repoName] = full_repo_name?.split("/") ?? []
  if (!owner || !repoName) {
    return createErrorResponse("Invalid repository name")
  }

  try {
    const response = await fetch(`${cfapiUrl}/${CF_API}/create-pr`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Authorization: `Bearer ${session.token}`,
        "X-CodeFlash-Source": "webapp",
      },
      body: JSON.stringify({
        traceId,
        diffContents,
        prCommentFields: {
          ...prCommentFields,
          function_name: functionName,
          file_path: filePath,
          speedup_x: speedupX,
          speedup_pct: speedupPct,
        },
        generatedTests,
        existingTests,
        coverage_message,
        baseBranch,
        owner,
        repo: repoName,
        originalLineProfiler,
        optimizedLineProfiler,
      }),
    })

    if (response.ok || response.status === 202) {
      const contentType = response.headers.get("content-type")
      if (contentType?.includes("json")) {
        const data = await response.json()
        console.log("[createPullRequest] Success response from API:", {
          status: response.status,
          data,
          dataType: typeof data,
        })
        return createSuccessResponse(data)
      }
      const data = await response.text()
      console.log("[createPullRequest] Success response from API (text):", {
        status: response.status,
        data,
        dataType: typeof data,
      })
      return createSuccessResponse(data)
    }

    // Get error message from response
    let errorMessage = "Failed to create pull request"

    const contentType = response.headers.get("content-type")
    if (contentType?.includes("json")) {
      const error = await response.json()
      errorMessage = error.message || error.error || errorMessage
    } else if (contentType?.includes("html")) {
      // HTML error page - provide a meaningful message based on status
      if (response.status === 404) {
        errorMessage = "API endpoint not found. Please check if the API server is running."
      } else if (response.status === 502 || response.status === 503) {
        errorMessage = "API server is unavailable. Please try again later."
      } else {
        errorMessage = `Server error (${response.status}). Please try again later.`
      }
    } else {
      const text = await response.text()
      if (text && !text.includes("<!DOCTYPE") && !text.includes("<html")) {
        errorMessage = text
      }
    }

    // Log to Sentry
    Sentry.captureException(new Error(errorMessage), {
      extra: {
        status: response.status,
        traceId,
        owner,
        repo: repoName,
      },
    })

    return createErrorResponse(errorMessage)
  } catch (error: any) {
    const errorMessage = error?.message || "Something went wrong. Please try again."
    Sentry.captureException(error)
    return createErrorResponse(errorMessage)
  }
}

export async function setApprovalStatus(eventId: string, status: "approved" | "rejected") {
  const authorizedEvent = await getAuthorizedEventById(eventId, {
    select: { id: true },
  })
  if (!authorizedEvent) {
    return { success: false, error: "Unauthorized" }
  }
  if (!authorizedEvent.event) {
    return { success: false, error: "Event not found" }
  }

  try {
    const updated = await prisma.optimization_events.update({
      where: {
        id: eventId,
      },
      data: {
        status,
      },
    })
    return { success: true, event: updated }
  } catch (error) {
    console.error(`Error setting approval status to ${status}:`, error)
    return { success: false, error: error instanceof Error ? error.message : "Unknown error" }
  }
}

export async function addComment({ eventId, content }: { eventId: string; content: string }) {
  const authorizedEvent = await getAuthorizedEventById(eventId, {
    select: { id: true },
  })
  if (!authorizedEvent) {
    return { success: false, error: "Unauthorized" }
  }
  if (!authorizedEvent.event) {
    return { success: false, error: "Event not found" }
  }

  try {
    const comment = await prisma.comments.create({
      data: {
        optimization_event_id: eventId,
        author_user_id: authorizedEvent.accountContext.userId,
        content,
      },
    })
    return { success: true, comment }
  } catch (error) {
    console.error("Error adding comment:", error)
    return {
      success: false,
      error: error instanceof Error ? error.message : "Failed to add comment",
    }
  }
}

export async function getCommentsByEvent(eventId: string) {
  const authorizedEvent = await getAuthorizedEventById(eventId, {
    select: { id: true },
  })
  if (!authorizedEvent) {
    return {
      success: false,
      error: "Unauthorized",
    }
  }
  if (!authorizedEvent.event) {
    return {
      success: false,
      error: "Event not found",
    }
  }

  try {
    const comments = await prisma.comments.findMany({
      where: {
        optimization_event_id: eventId,
      },
      orderBy: {
        created_at: "asc",
      },
      include: {
        author: {
          select: {
            user_id: true,
            email: true,
            name: true,
            github_username: true,
          },
        },
      },
    })

    return { success: true, comments }
  } catch (error) {
    console.error("Error fetching comments:", error)
    return {
      success: false,
      error: error instanceof Error ? error.message : "Failed to fetch comments",
    }
  }
}

/**
 * Server-side function to fetch all data needed for the review page in parallel.
 * Called from the server component to eliminate the client-side data-fetching waterfall.
 */
export async function getReviewPageInitData(traceId: string) {
  const accountContext = await getActionAccountContext()
  if (!accountContext) {
    return null
  }

  // Fetch the optimization event
  const event = await getOptimizationEventById({
    payload: accountContext.payload,
    trace_id: traceId,
  })
  if (!event) {
    return { event: null, comments: [], stagingCode: null }
  }

  // If git_branch storage, fetch staging code + comments in parallel
  const metadata = (event.metadata as any) || {}
  if (event.staging_storage_type === "git_branch") {
    const stagingBranchName = metadata.staging_branch_name
    const repository = event.repository

    if (stagingBranchName && repository?.full_name && repository?.installation_id) {
      const [stagingCodeResult, commentsResult] = await Promise.all([
        getStagingCodeFromApi({
          stagingBranchName,
          baseBranch: event.baseBranch || "main",
          fullRepoName: repository.full_name,
          installationId: repository.installation_id,
          functionName: event.function_name || undefined,
          filePath: event.file_path || undefined,
        }),
        getCommentsByEvent(event.id),
      ])

      return {
        event,
        comments: commentsResult.success ? (commentsResult.comments ?? []) : [],
        stagingCode: stagingCodeResult.success ? (stagingCodeResult.data ?? null) : null,
      }
    }
  }

  // For plain_text storage, just fetch comments
  const commentsResult = await getCommentsByEvent(event.id)

  return {
    event,
    comments: commentsResult.success ? (commentsResult.comments ?? []) : [],
    stagingCode: null,
  }
}
