import { createMonitoringLogger } from '../monitoring/monitoring-logger'

export type SecurityEventType =
  | 'cron_auth_rejected'
  | 'cron_secret_missing'
  | 'cron_execution_locked'
  | 'cron_duplicate_ignored'
  | 'pdf_ssrf_blocked'
  | 'rate_limit_blocked'
  | 'ip_access_denied'
  | 'csrf_blocked'
  | 'auth_blocked'
  | 'request_validation_failed'

export type SecurityEventLevel = 'info' | 'warn' | 'error'

export interface SecurityEventContext {
  endpoint?: string
  reason?: string
  ip?: string
  limiter?: string
  requestId?: string
  [key: string]: unknown
}

interface SecurityCounterState {
  count: number
  windowStart: number
}

const logger = createMonitoringLogger('security')
const counters = new Map<SecurityEventType, SecurityCounterState>()
const lastAlertAt = new Map<SecurityEventType, number>()

const DEFAULT_WINDOW_MS = parseInt(process.env.SECURITY_METRICS_WINDOW_MS || '300000', 10)
const DEFAULT_ALERT_THRESHOLD = parseInt(process.env.SECURITY_ALERT_THRESHOLD_DEFAULT || '25', 10)
const ALERT_COOLDOWN_MS = parseInt(process.env.SECURITY_ALERT_COOLDOWN_MS || '900000', 10)

let cachedPayloadPromise: Promise<any> | null = null

function getWindowMs(): number {
  return Number.isFinite(DEFAULT_WINDOW_MS) && DEFAULT_WINDOW_MS > 0
    ? DEFAULT_WINDOW_MS
    : 300000
}

function getAlertThreshold(eventType: SecurityEventType): number {
  const specificEnvKey = `SECURITY_ALERT_THRESHOLD_${eventType.toUpperCase()}`
  const value = process.env[specificEnvKey]
  if (!value) return DEFAULT_ALERT_THRESHOLD

  const parsed = parseInt(value, 10)
  if (Number.isNaN(parsed) || parsed <= 0) return DEFAULT_ALERT_THRESHOLD
  return parsed
}

function getCounter(eventType: SecurityEventType, now: number): SecurityCounterState {
  const existing = counters.get(eventType)
  const windowMs = getWindowMs()
  if (!existing || now - existing.windowStart >= windowMs) {
    const reset = { count: 0, windowStart: now }
    counters.set(eventType, reset)
    return reset
  }
  return existing
}

function logEvent(level: SecurityEventLevel, message: string, context: SecurityEventContext): void {
  if (level === 'error') {
    logger.error(message, context)
    return
  }
  if (level === 'warn') {
    logger.warn(message, context)
    return
  }
  logger.info(message, context)
}

async function getPayloadInstance(): Promise<any> {
  if (!cachedPayloadPromise) {
    cachedPayloadPromise = (async () => {
      const { getPayload } = await import('payload')
      const config = (await import(/* @vite-ignore */ '@payload-config')).default
      return getPayload({ config })
    })().catch((error) => {
      cachedPayloadPromise = null
      throw error
    })
  }
  return cachedPayloadPromise
}

async function sendThresholdAlert(
  eventType: SecurityEventType,
  count: number,
  threshold: number,
  context: SecurityEventContext,
): Promise<void> {
  const now = Date.now()
  const previousAlert = lastAlertAt.get(eventType)
  if (previousAlert && now - previousAlert < ALERT_COOLDOWN_MS) {
    return
  }

  lastAlertAt.set(eventType, now)

  try {
    const payload = await getPayloadInstance()
    const { sendAlert } = await import('../alerting/alert-service')
    await sendAlert(payload, {
      level: count >= threshold * 2 ? 'error' : 'warning',
      title: `Security threshold reached: ${eventType}`,
      message: `Security event "${eventType}" occurred ${count} times in ${Math.round(getWindowMs() / 1000)}s.`,
      details: {
        eventType,
        count,
        threshold,
        ...context,
      },
    })
  } catch (error) {
    logger.error('Failed to send security threshold alert', {
      eventType,
      threshold,
      error: error instanceof Error ? error.message : String(error),
    })
  }
}

export function recordSecurityEvent(
  eventType: SecurityEventType,
  context: SecurityEventContext = {},
  level: SecurityEventLevel = 'warn',
): void {
  const now = Date.now()
  const counter = getCounter(eventType, now)
  counter.count++

  logEvent(level, `Security event: ${eventType}`, {
    eventType,
    count: counter.count,
    windowStart: new Date(counter.windowStart).toISOString(),
    ...context,
  })

  const threshold = getAlertThreshold(eventType)
  if (counter.count >= threshold) {
    void sendThresholdAlert(eventType, counter.count, threshold, context)
  }
}

export function getSecurityMetricsSnapshot(): {
  windowMs: number
  counters: Array<{
    eventType: SecurityEventType
    count: number
    windowStart: string
  }>
} {
  const result = Array.from(counters.entries()).map(([eventType, counter]) => ({
    eventType,
    count: counter.count,
    windowStart: new Date(counter.windowStart).toISOString(),
  }))

  return {
    windowMs: getWindowMs(),
    counters: result.sort((a, b) => b.count - a.count),
  }
}

export function resetSecurityObservabilityForTests(): void {
  counters.clear()
  lastAlertAt.clear()
  cachedPayloadPromise = null
}
