/**
 * OTel span + metric helpers (KAR-532).
 *
 * Three exports:
 *   - withDbSpan(operation, table, fn): wrap a Supabase query so it shows up
 *     as a child span with `db.operation` + `db.table` attributes.
 *   - withBusinessSpan(name, attributes, fn): wrap a use-case so it appears
 *     as a named span in the trace.
 *   - recordBusinessMetric: a small fixed registry of counters and
 *     histograms covering the pilot KPIs (evaluations.created,
 *     evaluation.score, import.duration). Adding a metric is one line.
 *
 * Why a thin wrapper instead of pasting trace.getTracer() inline: every
 * call-site otherwise duplicates the start/end/exception boilerplate and
 * gets it subtly wrong (forgotten end(), wrong status code on error).
 * The wrapper makes the contract one line and the test once.
 *
 * Adoption is opt-in per call site — existing Supabase calls keep working
 * without these helpers. Use them on hot paths first.
 */
import { trace, SpanStatusCode, metrics } from "@opentelemetry/api"

const TRACER_NAME = "kadi-backend"
const METER_NAME = "kadi-backend"

function tracer() {
  return trace.getTracer(TRACER_NAME)
}

function meter() {
  return metrics.getMeter(METER_NAME)
}

/**
 * Wrap a Supabase query so it shows up as its own child span.
 */
export async function withDbSpan<T>(
  operation: "select" | "insert" | "update" | "delete" | "rpc" | "upsert",
  table: string,
  fn: () => Promise<T>,
): Promise<T> {
  return tracer().startActiveSpan(
    `supabase.${operation}`,
    { attributes: { "db.system": "postgresql", "db.operation": operation, "db.table": table } },
    async (span) => {
      try {
        const result = await fn()
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        ;(span as any).setStatus({ code: SpanStatusCode.OK })
        return result
      } catch (err) {
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        ;(span as any).recordException(err)
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        ;(span as any).setStatus({
          code: SpanStatusCode.ERROR,
          message: err instanceof Error ? err.message : String(err),
        })
        throw err
      } finally {
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        ;(span as any).end()
      }
    },
  )
}

/**
 * Wrap an arbitrary business-logic block (use case, request handler) so it
 * appears as a named span in the trace tree.
 */
export async function withBusinessSpan<T>(
  name: string,
  attributes: Record<string, string | number | boolean>,
  fn: () => Promise<T>,
): Promise<T> {
  return tracer().startActiveSpan(name, { attributes }, async (span) => {
    try {
      const result = await fn()
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      ;(span as any).setStatus({ code: SpanStatusCode.OK })
      return result
    } catch (err) {
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      ;(span as any).recordException(err)
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      ;(span as any).setStatus({
        code: SpanStatusCode.ERROR,
        message: err instanceof Error ? err.message : String(err),
      })
      throw err
    } finally {
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      ;(span as any).end()
    }
  })
}

/**
 * Fixed registry of pilot KPIs. Counters tally events; histograms record
 * a numeric value per event. Adding a metric is one line.
 */
export const recordBusinessMetric = {
  evaluationCreated(attributes: Record<string, string> = {}) {
    meter().createCounter("kadi.evaluations.created").add(1, attributes)
  },
  evaluationScore(score: number, attributes: Record<string, string> = {}) {
    meter().createHistogram("kadi.evaluation.score").record(score, attributes)
  },
  importDuration(ms: number, attributes: Record<string, string> = {}) {
    meter()
      .createHistogram("kadi.import.duration_ms")
      .record(ms, attributes)
  },
  activeUsers(count: number, attributes: Record<string, string> = {}) {
    meter().createHistogram("kadi.active.users").record(count, attributes)
  },
}
