import { randomUUID } from 'node:crypto'

const counters = new Map()
const histograms = new Map()

function labelKey(labels = {}) {
  return Object.entries(labels)
    .sort(([a], [b]) => a.localeCompare(b))
    .map(([key, value]) => `${key}=${String(value)}`)
    .join(',')
}

function metricKey(name, labels = {}) {
  const suffix = labelKey(labels)
  return suffix ? `${name}{${suffix}}` : name
}

function prometheusLabels(labels = {}) {
  const entries = Object.entries(labels).sort(([a], [b]) => a.localeCompare(b))
  if (!entries.length) return ''
  return `{${entries.map(([key, value]) => `${key}="${String(value).replaceAll('\\', '\\\\').replaceAll('"', '\\"')}"`).join(',')}}`
}

export function incrementMetric(name, labels = {}, amount = 1) {
  const key = metricKey(name, labels)
  const current = counters.get(key) || { name, labels, value: 0 }
  current.value += amount
  counters.set(key, current)
}

export function observeHistogram(name, labels = {}, value = 0) {
  const key = metricKey(name, labels)
  const current = histograms.get(key) || { name, labels, count: 0, sum: 0 }
  current.count += 1
  current.sum += Number(value) || 0
  histograms.set(key, current)
}

export function observeHttpRequest({ method, route, statusCode, durationMs }) {
  const status = String(statusCode || 0)
  const family = status ? `${status[0]}xx` : 'unknown'
  const labels = {
    method: method || 'GET',
    route: route || '/',
    status: family,
  }
  incrementMetric('dlbdi_http_requests_total', labels)
  observeHistogram('dlbdi_http_request_duration_ms', labels, durationMs)
  if (statusCode >= 500) incrementMetric('dlbdi_http_5xx_total', labels)
}

export function renderPrometheusMetrics() {
  const lines = [
    '# HELP dlbdi_http_requests_total Total HTTP requests handled by the lite backend.',
    '# TYPE dlbdi_http_requests_total counter',
  ]
  for (const item of counters.values()) {
    lines.push(`${item.name}${prometheusLabels(item.labels)} ${item.value}`)
  }
  lines.push('# HELP dlbdi_http_request_duration_ms_sum Total HTTP request duration in milliseconds.')
  lines.push('# TYPE dlbdi_http_request_duration_ms summary')
  for (const item of histograms.values()) {
    lines.push(`${item.name}_count${prometheusLabels(item.labels)} ${item.count}`)
    lines.push(`${item.name}_sum${prometheusLabels(item.labels)} ${Math.round(item.sum * 1000) / 1000}`)
  }
  return `${lines.join('\n')}\n`
}

export function logStructured(level, message, fields = {}) {
  const payload = {
    level,
    message,
    timestamp: new Date().toISOString(),
    service: 'dlbdi-backend',
    ...fields,
  }
  const line = JSON.stringify(payload)
  if (level === 'error' || level === 'critical') console.error(line)
  else if (level === 'warning') console.warn(line)
  else console.log(line)
}

function sentryEndpoint(dsn) {
  try {
    const url = new URL(dsn)
    const projectId = url.pathname.replace(/^\/+/, '').split('/').pop()
    const publicKey = url.username
    if (!projectId || !publicKey) return null
    return {
      url: `${url.protocol}//${url.host}/api/${projectId}/store/`,
      publicKey,
    }
  } catch {
    return null
  }
}

export async function captureException(error, context = {}) {
  const eventId = randomUUID().replaceAll('-', '')
  const message = error?.message || String(error || 'Unknown error')
  logStructured('error', message, {
    eventId,
    errorCode: error?.code || 'internal_error',
    stack: process.env.NODE_ENV === 'production' ? undefined : error?.stack,
    ...context,
  })

  const dsn = process.env.SENTRY_DSN || process.env.BACKEND_SENTRY_DSN || ''
  const endpoint = dsn ? sentryEndpoint(dsn) : null
  if (!endpoint) return eventId

  const payload = {
    event_id: eventId,
    platform: 'javascript',
    logger: 'dlbdi-backend',
    level: 'error',
    environment: process.env.SENTRY_ENVIRONMENT || process.env.NODE_ENV || 'development',
    message,
    exception: {
      values: [{
        type: error?.name || 'Error',
        value: message,
        stacktrace: error?.stack ? { frames: [] } : undefined,
      }],
    },
    extra: context,
    timestamp: new Date().toISOString(),
  }

  try {
    await fetch(endpoint.url, {
      method: 'POST',
      headers: {
        'content-type': 'application/json',
        'x-sentry-auth': `Sentry sentry_version=7, sentry_key=${endpoint.publicKey}, sentry_client=dlbdi-lite/0.1`,
      },
      body: JSON.stringify(payload),
    })
  } catch (sentryError) {
    logStructured('warning', 'Failed to send error to Sentry', {
      eventId,
      errorCode: sentryError?.code || 'sentry_send_failed',
      detail: sentryError?.message || String(sentryError),
    })
  }
  return eventId
}
