import { prisma } from "@codeflash-ai/common"
import { sendSlackMessage } from "./slack_util.js"
import {
  requiresApproval,
  hasQualityMonitoring,
  getApprovalEmoji,
  getRejectionEmoji,
} from "../config/approval-config.js"
import {
  missingRequiredFields,
  optimizationNotFound,
  internalServerError,
} from "../exceptions/index.js"
const SLACK_CHANNEL = process.env.SLACK_APPROVAL_CHANNEL_ID || process.env.SLACK_CHANNEL_ID
const APPROVAL_EMOJI = getApprovalEmoji()
const REJECTION_EMOJI = getRejectionEmoji()

const CODEFLASH_APP_BASE_URL = (
  process.env.CODEFLASH_APP_URL || "https://app.codeflash.ai"
).replace(/\/$/, "") // Removes trailing slash if present

// Check if the given organization and repository are for quality monitoring
export function isQualityMonitoringRepo(owner: string, repo: string): boolean {
  return hasQualityMonitoring(owner, repo)
}

// Check if the given organization and repository require approval
export function requiresApprovalForRepo(owner: string, repo: string): boolean {
  // Flag to override the approval system
  if (process.env.DISABLE_APPROVAL_SYSTEM === "true") {
    return false
  }

  // Quality monitoring repos don't require approval but get notifications
  if (isQualityMonitoringRepo(owner, repo)) {
    return false
  }

  return requiresApproval(owner, repo)
}

/**
 * Send quality monitoring notification for an optimization (non-blocking)
 *
 * @param {string} traceId - The trace ID of the optimization
 * @param {string} owner - Repository owner/organization
 * @param {string} repo - Repository name
 * @param {string} functionName - The function being optimized
 * @param {string} userId - The requesting user's ID
 * @param {object} requestData - Additional optimization details, including prCommentFields and diffContents
 * @param {string} prUrl - The URL of the created PR
 * @returns {Promise<boolean>} - True if successful
 */
export async function sendQualityMonitoringNotification(
  traceId: string,
  owner: string,
  repo: string,
  functionName: string,
  userId: string,
  requestData: any,
  prUrl?: string,
): Promise<boolean> {
  try {
    const prType = requestData.type === "create-pr" ? "PR Creation" : "PR Change Suggestion"
    const prCommentFields = requestData.prCommentFields || {}
    const traceViewUrl = `${CODEFLASH_APP_BASE_URL}/trace/${traceId}`

    // Store quality monitoring data in DB for trace link access in slack
    await prisma.optimization_features.upsert({
      where: { trace_id: traceId },
      update: {
        approval_required: false, // Not approval, just Quanlity monitoring
        approval_status: "quality_monitoring", // Special status to distinguish from approval
        organization: owner,
        repository: repo,
        experiment_metadata: requestData,
      },
      create: {
        trace_id: traceId,
        approval_required: false,
        approval_status: "quality_monitoring",
        organization: owner,
        repository: repo,
        user_id: userId,
        experiment_metadata: requestData,
      },
    })

    const blocks: any[] = [
      {
        type: "header",
        text: {
          type: "plain_text",
          text: `🔍 Quality Monitoring: ${prType} Applied`,
          emoji: true,
        },
      },
      {
        type: "section",
        fields: [
          { type: "mrkdwn", text: `*Repository:*\n📁 ${owner}/${repo}` },
          { type: "mrkdwn", text: `*Function:*\n🔧 \`${functionName}\`` },
        ],
      },
      {
        type: "section",
        fields: [
          { type: "mrkdwn", text: `*Speedup:*\n🚀 ${prCommentFields.speedup_pct || "N/A"}` },
          {
            type: "mrkdwn",
            text: `*Orig. Runtime:*\n⏱️ ${prCommentFields.original_runtime || "N/A"}`,
          },
        ],
      },
      {
        type: "section",
        fields: [
          { type: "mrkdwn", text: `*User ID:*\n👤 ${userId}` },
          { type: "mrkdwn", text: `*Trace ID:*\n🏷️ \`${traceId}\`` },
          ...(requestData.optimizationReview
            ? [
                {
                  type: "mrkdwn",
                  text: `*Optimization Review Rating:*\n🎯 \`${requestData.optimizationReview}\``,
                },
              ]
            : []),
        ],
      },
    ]

    // Add explanation snippet if available
    const explanation = prCommentFields.optimization_explanation
    if (explanation) {
      const explanationSnippet = `${explanation.substring(0, 280)}${explanation.length > 280 ? "..." : ""}`
      blocks.push({
        type: "section",
        text: {
          type: "mrkdwn",
          text: `*Explanation Snippet:*\n>>>${explanationSnippet}`,
        },
      })
    }

    // Add buttons for viewing details and PR
    const actionElements: any[] = [
      {
        type: "button",
        text: {
          type: "plain_text",
          text: "View Full Diff & Details 📄",
          emoji: true,
        },
        url: traceViewUrl,
        style: "primary",
        action_id: `view_trace_details_${traceId.replace(/\./g, "_")}`,
      },
    ]

    if (prUrl) {
      actionElements.push({
        type: "button",
        text: {
          type: "plain_text",
          text: "View PR 🔗",
          emoji: true,
        },
        url: prUrl,
        action_id: `view_pr_${traceId.replace(/\./g, "_")}`,
      })
    }

    blocks.push({
      type: "actions",
      elements: actionElements,
    })

    blocks.push({ type: "divider" })

    blocks.push({
      type: "context",
      elements: [
        {
          type: "mrkdwn",
          text: "🔍 *Quality Monitoring Mode:* This optimization was automatically applied for quality review purposes. No approval required.",
        },
      ],
    })

    const message = {
      blocks,
      text: `Quality Monitoring: ${prType} Applied for ${functionName} in ${owner}/${repo} (${traceId}). Speedup: ${prCommentFields.speedup_pct || "N/A"}. View details: ${traceViewUrl}${prUrl ? ` | PR: ${prUrl}` : ""}`,
    }

    // Send message to Slack (non-blocking)
    await sendSlackMessage(message, SLACK_CHANNEL, false) // false = don't require reactions

    console.log(`Quality monitoring notification sent for ${owner}/${repo} optimization ${traceId}`)
    return true
  } catch (error) {
    console.error(`Error sending quality monitoring notification for trace ${traceId}: ${error}`)
    return false
  }
}

/**
 * Request approval for an optimization
 *
 * @param {string} traceId - The trace ID of the optimization
 * @param {string} owner - Repository owner/organization
 * @param {string} repo - Repository name
 * @param {string} functionName - The function being optimized
 * @param {string} userId - The requesting user's ID
 * @param {object} requestData - Additional optimization details, including prCommentFields and diffContents
 * @returns {Promise<boolean>} - True if successful
 */
export async function requestApproval(
  traceId: string,
  owner: string,
  repo: string,
  functionName: string,
  userId: string,
  requestData: any,
): Promise<boolean> {
  try {
    await prisma.optimization_features.upsert({
      where: { trace_id: traceId },
      update: {
        approval_required: true,
        approval_status: "pending",
        organization: owner,
        repository: repo,
        experiment_metadata: requestData,
      },
      create: {
        trace_id: traceId,
        approval_required: true,
        approval_status: "pending",
        organization: owner,
        repository: repo,
        user_id: userId,
        experiment_metadata: requestData,
      },
    })

    const prType = requestData.type === "create-pr" ? "PR Creation" : "PR Change Suggestion"
    const prCommentFields = requestData.prCommentFields || {}
    const traceViewUrl = `${CODEFLASH_APP_BASE_URL}/trace/${traceId}`

    const blocks: any[] = [
      {
        type: "header",
        text: {
          type: "plain_text",
          text: `⚠️ ${prType} Optimization Approval Request`,
          emoji: true,
        },
      },
      {
        type: "section",
        fields: [
          { type: "mrkdwn", text: `*Repository:*\n📁 ${owner}/${repo}` },
          { type: "mrkdwn", text: `*Function:*\n🔧 \`${functionName}\`` },
        ],
      },
      {
        type: "section",
        fields: [
          { type: "mrkdwn", text: `*Speedup:*\n🚀 ${prCommentFields.speedup_pct || "N/A"}` },
          {
            type: "mrkdwn",
            text: `*Orig. Runtime:*\n⏱️ ${prCommentFields.original_runtime || "N/A"}`,
          },
        ],
      },
      {
        type: "section",
        fields: [
          { type: "mrkdwn", text: `*User ID:*\n👤 ${userId}` },
          { type: "mrkdwn", text: `*Trace ID:*\n🏷️ \`${traceId}\`` }, // Changed emoji
          ...(requestData.optimizationReview
            ? [
                {
                  type: "mrkdwn",
                  text: `*Optimization Review Rating:*\n🎯 \`${requestData.optimizationReview}\``,
                },
              ]
            : []),
        ],
      },
    ]

    // Add explanation snippet if available
    const explanation = prCommentFields.optimization_explanation
    if (explanation) {
      const explanationSnippet = `${explanation.substring(0, 280)}${explanation.length > 280 ? "..." : ""}`
      blocks.push({
        type: "section",
        text: {
          type: "mrkdwn",
          text: `*Explanation Snippet:*\n>>>${explanationSnippet}`,
        },
      })
    }

    // Add "View Full Diff & Details" button
    blocks.push({
      type: "actions",
      elements: [
        {
          type: "button",
          text: {
            type: "plain_text",
            text: "View Full Diff & Details 📄",
            emoji: true,
          },
          url: traceViewUrl,
          style: "primary", // Makes the button blue and prominent
          // Replace dots in traceId for action_id as they can be problematic
          action_id: `view_trace_details_${traceId.replace(/\./g, "_")}`,
        },
      ],
    })

    blocks.push({ type: "divider" })

    blocks.push({
      type: "context",
      elements: [
        {
          type: "mrkdwn",
          text: `:${APPROVAL_EMOJI}: to approve | :${REJECTION_EMOJI}: to reject`,
        },
      ],
    })

    const message = {
      blocks,
      text: `${prType} Optimization Approval Request for ${functionName} in ${owner}/${repo} (${traceId}). Speedup: ${prCommentFields.speedup_pct || "N/A"}. View details: ${traceViewUrl}`,
    }

    // Send message to Slack
    const response = await sendSlackMessage(message, SLACK_CHANNEL, true)

    if (response && typeof (response as any).ts === "string") {
      await prisma.optimization_features.update({
        where: { trace_id: traceId },
        data: { slack_message_ts: (response as any).ts },
      })
    }

    return true
  } catch (error) {
    console.error(`Error requesting approval for trace ${traceId}: ${error}`)
    return false
  }
}

/**
 * Check the approval status of an optimization
 */
export async function checkApprovalStatus(req, res) {
  try {
    const { traceId } = req.query

    if (!traceId) {
      throw missingRequiredFields("traceId")
    }

    // Find the optimization record
    const optimization = await prisma.optimization_features.findUnique({
      where: { trace_id: traceId as string },
      select: {
        approval_required: true,
        approval_status: true,
        approval_user: true,
        approval_timestamp: true,
        organization: true,
        repository: true,
      },
    })

    if (!optimization) {
      throw optimizationNotFound(traceId as string)
    }

    // If approval not required, consider it approved
    if (!optimization.approval_required) {
      return res.json({
        status: "approved",
        message: "Approval not required for this optimization",
        details: optimization,
      })
    }

    // For current status
    const statusMessages = {
      pending: "This optimization is pending approval",
      approved: "This optimization has been approved",
      rejected: "This optimization was rejected",
    }

    return res.json({
      status: optimization.approval_status || "pending",
      message:
        statusMessages[optimization.approval_status as keyof typeof statusMessages] ||
        "Unknown status",
      details: optimization,
    })
  } catch (error) {
    if (error && typeof error === "object" && "getHttpStatus" in error) {
      throw error
    }
    console.error(`Error checking approval status: ${error}`)
    throw internalServerError("Error checking approval status")
  }
}

/**
 * Process approval or rejection based on emoji reactions
 *
 * @param {object} event - Slack reaction event
 * @returns {Promise<boolean>} - True if processed successfully
 */
export async function processReaction(event: any): Promise<boolean> {
  // Consider more specific type for event
  try {
    // Only handle reaction_added events on messages
    if (event.type !== "reaction_added" || event.item.type !== "message") {
      return false
    }

    const { reaction, user, item } = event
    const { ts, channel } = item

    // Find optimization with this message timestamp - ONLY for approval required messages
    const optimization = await prisma.optimization_features.findFirst({
      where: {
        slack_message_ts: ts,
        approval_required: true, // CRITICAL: Only process reactions for approval messages
        approval_status: "pending", // Only process reactions for pending approvals
      },
    })

    if (!optimization) {
      return false // Not one of our approval messages or already processed
    }

    // Double-check: Ensure this is not a quality monitoring message
    if (optimization.approval_status === "quality_monitoring") {
      console.log(
        `Ignoring reaction on quality monitoring message for trace ${optimization.trace_id}`,
      )
      return false
    }

    // Process approval
    if (reaction === APPROVAL_EMOJI) {
      await prisma.optimization_features.update({
        where: { trace_id: String(optimization.trace_id) },
        data: {
          approval_status: "approved",
          approval_user: user,
          approval_timestamp: new Date(),
        },
      })

      // Send confirmation
      await sendSlackMessage(
        {
          blocks: [
            {
              type: "section",
              text: {
                type: "mrkdwn",
                text: `:${APPROVAL_EMOJI}: Optimization \`${optimization.trace_id}\` has been *approved* by <@${user}>.`,
              },
            },
          ],
          text: `Optimization ${optimization.trace_id} has been approved by <@${user}>.`,
        },
        channel,
      )

      // Get the stored request data
      const requestData = optimization.experiment_metadata as Record<string, any>
      if (requestData) {
        try {
          // Process based on request type
          if (requestData.type === "create-pr") {
            const { triggerCreatePr } = await import("../endpoints/create-pr.js")

            // Get user's nickname - needed for PR
            const userNickname = await getUserNickname(requestData.userId)
            if (!userNickname) {
              console.error(`Could not get nickname for user ${requestData.userId}`)
              return false
            }

            // Get installation octokit
            const installationOctokit = await getInstallationOctokit(
              requestData.owner,
              requestData.repo,
              requestData.userId,
            )
            if (!installationOctokit || installationOctokit instanceof Error) {
              console.error(
                `Could not get installation octokit for ${requestData.owner}/${requestData.repo}: ${installationOctokit instanceof Error ? installationOctokit.message : ""}`,
              )
              return false
            }

            // Trigger PR creation
            await triggerCreatePr(
              requestData.owner,
              requestData.repo,
              requestData.baseBranch,
              requestData.diffContents,
              requestData.prCommentFields,
              requestData.existingTests,
              requestData.generatedTests,
              requestData.coverage_message,
              requestData.userId,
              userNickname,
              installationOctokit,
              requestData.replayTests,
              requestData.concolicTests,
              String(optimization.trace_id),
              requestData.optimizationReview,
            )
          } else if (requestData.type === "suggest-pr-changes") {
            const { triggerSuggestPrChanges } = await import("../endpoints/suggest-pr-changes.js")

            // Get user's nickname - needed for PR
            const userNickname = await getUserNickname(requestData.userId)
            if (!userNickname) {
              console.error(`Could not get nickname for user ${requestData.userId}`)
              return false
            }

            // Get installation octokit
            const installationOctokit = await getInstallationOctokit(
              requestData.owner,
              requestData.repo,
              requestData.userId,
            )
            if (!installationOctokit || installationOctokit instanceof Error) {
              console.error(
                `Could not get installation octokit for ${requestData.owner}/${requestData.repo}: ${installationOctokit instanceof Error ? installationOctokit.message : ""}`,
              )
              return false
            }

            // Trigger PR suggestion
            await triggerSuggestPrChanges(
              requestData.owner,
              requestData.repo,
              requestData.pullNumber,
              requestData.diffContents,
              requestData.prCommentFields,
              requestData.existingTests,
              requestData.generatedTests,
              requestData.coverage_message,
              requestData.userId,
              userNickname,
              installationOctokit,
              requestData.replayTests,
              requestData.concolicTests,
              String(optimization.trace_id),
              requestData.optimizationReview,
            )
          }
        } catch (err: any) {
          console.error(
            `Error processing approved request for trace ${optimization.trace_id}: ${err}`,
          )

          // Extract helpful error details for Slack notification
          const errorMessage = err.message || String(err)
          const errorType = err.constructor?.name || "Error"
          const isPrMergedOrClosed =
            errorMessage.includes("merged") || errorMessage.includes("closed")

          const errorBlocks: any[] = [
            {
              type: "section",
              text: {
                type: "mrkdwn",
                text: `:warning: Error processing approved optimization \`${optimization.trace_id}\`:`,
              },
            },
            {
              type: "section",
              text: {
                type: "mrkdwn",
                text: `\`\`\`${errorMessage}\`\`\``,
              },
            },
          ]

          // Add helpful context if PR is merged/closed
          if (isPrMergedOrClosed) {
            errorBlocks.push({
              type: "context",
              elements: [
                {
                  type: "mrkdwn",
                  text: `ℹ️ The target PR may have been merged or closed since the optimization was queued for approval.`,
                },
              ],
            })
          }

          await sendSlackMessage(
            {
              blocks: errorBlocks,
              text: `Error processing optimization ${optimization.trace_id}: ${errorMessage}`,
            },
            channel,
          )

          // Return false to indicate the reaction processing failed
          return false
        }
      }

      return true
    }

    // Process rejection
    if (reaction === REJECTION_EMOJI) {
      await prisma.optimization_features.update({
        where: { trace_id: String(optimization.trace_id) },
        data: {
          approval_status: "rejected",
          approval_user: user,
          approval_timestamp: new Date(),
        },
      })

      await sendSlackMessage(
        {
          blocks: [
            {
              type: "section",
              text: {
                type: "mrkdwn",
                text: `:${REJECTION_EMOJI}: Optimization \`${optimization.trace_id}\` has been *rejected* by <@${user}>.`,
              },
            },
          ],
          text: `Optimization ${optimization.trace_id} has been rejected by <@${user}>.`,
        },
        channel,
      )

      return true
    }

    return false
  } catch (error) {
    console.error(`Error processing reaction: ${error}`)
    return false
  }
}

// Helper functions (ensure these are correctly typed and paths are valid)
async function getUserNickname(userId: string): Promise<string | null> {
  const { userNickname } = await import("../auth0-mgmt.js")
  return await userNickname(userId)
}

async function getInstallationOctokit(
  owner: string,
  repo: string,
  userId?: string,
): Promise<any | Error> {
  const { getInstallationOctokitByOwner } = await import("../github/github-utils.js")
  const { githubApp } = await import("../github/github-app.js")
  return await getInstallationOctokitByOwner(githubApp, owner, repo, userId)
}
