import { Request, Response } from "express"
import * as crypto from "node:crypto"
import { posthog } from "../analytics.js"
import { processReaction } from "../github/optimization_approval.js"
import * as Sentry from "@sentry/node"
import { logger } from "../utils/logger.js"
import { forbidden, internalServerError } from "../exceptions/index.js"

// Dependencies interface for easier testing
export interface HandleSlackEventsDependencies {
  crypto: typeof crypto
  posthog: typeof posthog
  processReaction: typeof processReaction
  Sentry: typeof Sentry
  getSlackSigningSecret: () => string | undefined
  getCurrentTime: () => number
  console: typeof console
}

// Default dependencies
let dependencies: HandleSlackEventsDependencies = {
  crypto,
  posthog,
  processReaction,
  Sentry,
  getSlackSigningSecret: () => process.env.SLACK_SIGNING_SECRET,
  getCurrentTime: () => Math.floor(Date.now() / 1000),
  console,
}

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

export function resetHandleSlackEventsDependencies() {
  dependencies = {
    crypto,
    posthog,
    processReaction,
    Sentry,
    getSlackSigningSecret: () => process.env.SLACK_SIGNING_SECRET,
    getCurrentTime: () => Math.floor(Date.now() / 1000),
    console,
  }
}

/**
 * Verify that the request is coming from Slack
 */
export function verifySlackRequest(req: Request): boolean {
  const SLACK_SIGNING_SECRET = dependencies.getSlackSigningSecret()

  if (!SLACK_SIGNING_SECRET) {
    logger.warn("SLACK_SIGNING_SECRET not configured. Skipping request verification.", req)
    return true
  }

  const slackSignature = req.headers["x-slack-signature"] as string
  const slackTimestamp = req.headers["x-slack-request-timestamp"] as string

  if (!slackSignature || !slackTimestamp) {
    return false
  }

  // Prevent replay attacks - reject requests older than 5 minutes
  const currentTime = dependencies.getCurrentTime()
  if (Math.abs(currentTime - parseInt(slackTimestamp)) > 300) {
    return false
  }

  const requestBody = typeof req.body === "string" ? req.body : JSON.stringify(req.body)

  const baseString = `v0:${slackTimestamp}:${requestBody}`

  const hmac = dependencies.crypto.createHmac("sha256", SLACK_SIGNING_SECRET)
  const signature = `v0=${hmac.update(baseString).digest("hex")}`

  return dependencies.crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(slackSignature))
}

/**
 * Handle Slack events for the approval system
 */
export async function handleSlackEvents(req: Request, res: Response) {
  try {
    // Verify the request is from Slack
    if (!verifySlackRequest(req)) {
      logger.error("Failed to verify Slack request", req)
      throw forbidden("Invalid Slack request signature")
    }

    // Handle URL verification (required when setting up events)
    if (req.body.type === "url_verification") {
      logger.info("Handling Slack URL verification challenge", req)
      return res.json({ challenge: req.body.challenge })
    }

    // Acknowledge receipt quickly
    res.status(200).send("OK")

    const event = req.body.event
    if (event) {
      logger.info(`Processing Slack event: ${event.type}`, req, {
        eventType: event.type,
      })

      if (event.type === "reaction_added") {
        const processed = await dependencies.processReaction(event)

        if (processed) {
          dependencies.posthog?.capture({
            distinctId: "system",
            event: "slack-approval-reaction-processed",
            properties: {
              reaction: event.reaction,
              result: "success",
            },
          })
        }
      }
    }
  } catch (error) {
    // Re-throw AppExceptions to be handled by GlobalExceptionHandler if headers not sent
    if (error && typeof error === "object" && "getHttpStatus" in error) {
      if (!res.headersSent) {
        throw error
      }
      // If headers already sent, just log it
      logger.errorWithSentry(`Error handling Slack event: ${error}`, req, {}, error as Error)
      dependencies.Sentry.captureException(error)
      return
    }

    logger.errorWithSentry(`Error handling Slack event: ${error}`, req, {}, error as Error)

    // If we haven't sent a response yet, throw an error
    if (!res.headersSent) {
      throw internalServerError("Error processing Slack event")
    }
    dependencies.Sentry.captureException(error)
    // Log to monitoring
    dependencies.posthog?.capture({
      distinctId: "system",
      event: "slack-optimization-approval-error",
      properties: {
        error: String(error),
      },
    })
  }
}
