import { AppErrorAction } from "./app-error-action.js"

/**
 * Context map for tracking request context across the exception lifecycle
 */
export interface ExceptionContext {
  requestId?: string
  traceId?: string
  correlationId?: string
  userId?: string
  endpoint?: string
  /** Force logging to Sentry even if error action is DEFAULT */
  shouldLogToSentry?: boolean
  [key: string]: unknown
}

/**
 * Base exception class - abstract class that all application exceptions extend
 * Similar to Java BaseException
 */
export abstract class BaseException extends Error {
  /** Context map for request tracking */
  protected contextMap: ExceptionContext

  constructor(message: string, context?: ExceptionContext) {
    super(message)
    this.name = this.constructor.name
    this.contextMap = context ?? {}

    // Maintains proper stack trace for where our error was thrown
    Error.captureStackTrace(this, this.constructor)
  }

  /** Get HTTP status code */
  abstract getHttpStatus(): number

  /** Get application error code */
  abstract getAppErrorCode(): string

  /** Get error action */
  abstract getErrorAction(): AppErrorAction

  /** Get error title */
  abstract getTitle(): string

  /** Get error type */
  abstract getErrorType(): string

  /** Get downstream error message (for wrapped external errors) */
  abstract getDownstreamErrorMessage(): string | null

  /** Get downstream error code (for wrapped external errors) */
  abstract getDownstreamErrorCode(): string | null

  /** Get context map */
  getContextMap(): ExceptionContext {
    return this.contextMap
  }

  /** Set context value */
  setContext(key: string, value: unknown): void {
    this.contextMap[key] = value
  }

  /** Get context value */
  getContext(key: string): unknown {
    return this.contextMap[key]
  }

  /** Check if this exception should be logged to Sentry regardless of error action */
  getShouldLogToSentry(): boolean {
    return this.contextMap.shouldLogToSentry === true
  }
}
