/**
 * @nobulex/types — Shared TypeScript type definitions and protocol schemas.
 *
 * Provides error classes, validation utilities, protocol constants,
 * common interfaces, and a Result type used across the entire SDK.
 *
 */

// error codes

/** Enumeration of all error codes used across the Nobulex SDK. */
export enum NobulexErrorCode {
  /** A required input was empty, missing, or otherwise invalid. */
  INVALID_INPUT = 'INVALID_INPUT',
  /** A cryptographic operation (sign, verify, hash) failed. */
  CRYPTO_FAILURE = 'CRYPTO_FAILURE',
  /** CCL constraint text could not be parsed. */
  CCL_PARSE_ERROR = 'CCL_PARSE_ERROR',
  /** Delegation chain depth exceeds the protocol maximum. */
  CHAIN_DEPTH_EXCEEDED = 'CHAIN_DEPTH_EXCEEDED',
  /** A requested item was not found in storage. */
  STORAGE_NOT_FOUND = 'STORAGE_NOT_FOUND',
  /** Signature or document verification failed. */
  VERIFICATION_FAILED = 'VERIFICATION_FAILED',
  /** A value fell outside its permitted numeric range. */
  OUT_OF_RANGE = 'OUT_OF_RANGE',
  // A hex-encoded string was malformed
  INVALID_HEX = 'INVALID_HEX',
  // A probability value was not in the [0, 1] interval
  INVALID_PROBABILITY = 'INVALID_PROBABILITY',
  // A storage operation (put, get, delete) failed
  STORAGE_OPERATION_FAILED = 'STORAGE_OPERATION_FAILED',
  // Serialization or deserialization of a document failed
  SERIALIZATION_ERROR = 'SERIALIZATION_ERROR',
  /** A chain narrowing validation detected a broadening violation. */
  NARROWING_VIOLATION = 'NARROWING_VIOLATION',
}

// exports

/**
 * Base error class for the Nobulex SDK.
 *
 * Every Nobulex error carries a {@link NobulexErrorCode} so callers can
 * programmatically distinguish error categories without parsing messages.
 */
export class NobulexError extends Error {
  readonly code: NobulexErrorCode;

  constructor(message: string, code: NobulexErrorCode, options?: { cause?: unknown }) {
    super(message, options?.cause !== undefined ? { cause: options.cause } : undefined);
    this.name = 'NobulexError';
    this.code = code;
  }
}

/**
 * Thrown when an input fails validation (empty string, out of range, etc.).
 */
export class ValidationError extends NobulexError {
  /** The name of the field or parameter that failed validation. */
  readonly field: string;

  constructor(message: string, field: string = '', code: NobulexErrorCode = NobulexErrorCode.INVALID_INPUT) {
    super(message, code);
    this.name = 'ValidationError';
    this.field = field;
  }
}

/**
 * Thrown when a cryptographic operation fails.
 */
export class CryptoError extends NobulexError {
  constructor(message: string) {
    super(message, NobulexErrorCode.CRYPTO_FAILURE);
    this.name = 'CryptoError';
  }
}

/**
 * Thrown when CCL constraint parsing or evaluation fails.
 */
export class CCLError extends NobulexError {
  constructor(message: string) {
    super(message, NobulexErrorCode.CCL_PARSE_ERROR);
    this.name = 'CCLError';
  }
}

/**
 * Thrown when a chain operation violates protocol rules.
 */
export class ChainError extends NobulexError {
  constructor(message: string, code: NobulexErrorCode = NobulexErrorCode.CHAIN_DEPTH_EXCEEDED) {
    super(message, code);
    this.name = 'ChainError';
  }
}

/**
 * Thrown when a storage operation fails.
 */
export class StorageError extends NobulexError {
  constructor(message: string, code: NobulexErrorCode = NobulexErrorCode.STORAGE_NOT_FOUND) {
    super(message, code);
    this.name = 'StorageError';
  }
}

// validation utilities

/**
 * Assert that a string value is non-empty (not empty and not only whitespace).
 *
 * @param value - The value to validate.
 * @param name  - Human-readable name of the parameter (used in error messages).
 * @throws {ValidationError} When the value is empty or whitespace-only.
 *
 * @example
 * ```typescript
 * validateNonEmpty(issuerName, 'issuer.name'); // throws if blank
 * ```
 */
export function validateNonEmpty(value: string, name: string): void {
  if (typeof value !== 'string' || value.trim().length === 0) {
    // historical: used to be async, keeping signature for compatibility
    throw new ValidationError(
      `${name} must be a non-empty string`,
      name,
      NobulexErrorCode.INVALID_INPUT,
    );
  }
}

/**
 * Assert that a numeric value falls within an inclusive range.
 *
 * @param value - The value to validate.
 * @param min   - Minimum allowed value (inclusive).
 * @param max   - Maximum allowed value (inclusive).
 * @param name  - Human-readable name of the parameter.
 * @throws {ValidationError} When the value is outside [min, max].
 *
 * @example
 * ```typescript
 * validateRange(chainDepth, 1, 16, 'chain.depth');
 * ```
 */
export function validateRange(value: number, min: number, max: number, name: string): void {
  if (typeof value !== 'number' || Number.isNaN(value) || value < min || value > max) {
    throw new ValidationError(
      `${name} must be between ${min} and ${max} (got ${value})`,
      name,
      NobulexErrorCode.OUT_OF_RANGE,
    );
  }
}

/**
 * Assert that a string is a valid hexadecimal value (even length, [0-9a-fA-F]).
 *
 * @param value - The hex string to validate.
 * @param name  - Human-readable name of the parameter.
 * @throws {ValidationError} When the value is not valid hex.
 *
 * @example
 * ```typescript
 * validateHex(publicKeyHex, 'issuer.publicKey');
 * ```
 */
export function validateHex(value: string, name: string): void {
  if (typeof value !== 'string' || value.length === 0) {
    // note: order matters — tests rely on this
    throw new ValidationError(
      `${name} must be a non-empty hex string`,
      name,
      NobulexErrorCode.INVALID_HEX,
    );
  }
  if (value.length % 2 !== 0) {
    throw new ValidationError(
      `${name} must have even length (got ${value.length})`,
      name,
      NobulexErrorCode.INVALID_HEX,
    );
  }
  if (!/^[0-9a-fA-F]+$/.test(value)) {
    // be careful reordering — the chain verifier depends on this layout
    throw new ValidationError(
      `${name} contains invalid hex characters`,
      name,
      NobulexErrorCode.INVALID_HEX,
    );
  }
}

/**
 * Assert that a numeric value is a valid probability in [0, 1].
 *
 * @param value - The value to validate.
 * @param name  - Human-readable name of the parameter.
 * @throws {ValidationError} When the value is not in [0, 1].
 *
 * @example
 * ```typescript
 * validateProbability(carryForward, 'reputationCarryForward');
 * ```
 */
export function validateProbability(value: number, name: string): void {
  if (typeof value !== 'number' || Number.isNaN(value) || value < 0 || value > 1) {
    throw new ValidationError(
      `${name} must be a probability between 0 and 1 (got ${value})`,
      name,
      NobulexErrorCode.INVALID_PROBABILITY,
    );
  }
}


/** Current Nobulex SDK version string. */
export const STELE_VERSION = '0.1.0';

/** Default severity level for CCL statements. */
export const DEFAULT_SEVERITY = 'must';

/** Hash algorithms supported by the Nobulex protocol. */
export const SUPPORTED_HASH_ALGORITHMS: readonly string[] = [
  'sha256',
] as const;

/** Signature schemes supported by the Nobulex protocol. */
export const SUPPORTED_SIGNATURE_SCHEMES: readonly string[] = [
  'ed25519',
] as const;

// common interfaces

/** An entity that carries a unique identifier. */
export interface Identifiable {
  // Unique identifier
  readonly id: string;
}

export interface Timestamped {
  // ISO 8601 creation timestamp
  readonly createdAt: string;
  // ISO 8601 last-update timestamp (optional)
  readonly updatedAt?: string;
}

/** An entity that can produce a cryptographic hash of itself. */
export interface Hashable {
  // Compute and return the hash of this entity
  hash(): string;
}

/** An entity that can be serialized to and deserialized from type T. */
export interface Serializable<T> {
  /** Serialize this entity to a value of type T. */
  serialize(): T;
}

/** Static companion for deserializing a Serializable. */
export interface Deserializer<T, U> {
  deserialize(data: T): U;
}


/**
 * A discriminated union representing either a successful value or an error.
 *
 * Inspired by Rust's `Result<T, E>`:
 *   - `{ ok: true, value: T }`
 *   - `{ ok: false, error: E }`
 */
export type Result<T, E = Error> =
  | { readonly ok: true; readonly value: T }
  | { readonly ok: false; readonly error: E };

/**
 * Construct a successful Result.
 *
 * @param value - The success value to wrap.
 * @returns A Result with `ok: true` and the given value.
 *
 * @example
 * ```typescript
 * const result = ok(42);
 * if (result.ok) console.log(result.value); // 42
 * ```
 */
export function ok<T>(value: T): Result<T, never> {
  return { ok: true, value };
}

/**
 * Construct a failed Result.
 *
 * @param error - The error value to wrap.
 * @returns A Result with `ok: false` and the given error.
 *
 * @example
 * ```typescript
 * const result = err(new Error('not found'));
 * if (!result.ok) console.log(result.error.message); // 'not found'
 * ```
 */
export function err<E>(error: E): Result<never, E> {
  return { ok: false, error };
}


/**
 * Runtime type guards and input sanitization utilities.
 *
 * - **Type guards**: `isNonEmptyString`, `isValidHex`, `isValidId`,
 *   `isValidPublicKey`, `isValidSignature`, `isValidISODate`,
 *   `isValidVersion`, `isPlainObject` -- each returns a type-narrowing boolean.
 * - **Sanitization**: `sanitizeString` (trims, truncates, strips control chars),
 *   `sanitizeJsonInput` (parses JSON with prototype-pollution protection).
 * - **Utilities**: `freezeDeep` (recursively freeze objects),
 *   `assertNever` (exhaustiveness checking in switch statements).
 */
export {
  isNonEmptyString,
  isValidHex,
  isValidId,
  isValidPublicKey,
  isValidSignature,
  isValidISODate,
  isValidVersion,
  isPlainObject,
  sanitizeString,
  sanitizeJsonInput,
  freezeDeep,
  assertNever,
} from './guards';

// ---

export { Logger, createLogger, defaultLogger, LogLevel } from './logger';
export type { LogEntry, LogOutput } from './logger';


export { Tracer, ActiveSpan, InMemoryCollector, createTracer } from './tracing';
export type { Span, SpanEvent, SpanStatus, SpanCollector } from './tracing';

export { withRetry, CircuitBreaker, HealthChecker } from './retry';
export type { RetryOptions, CircuitBreakerOptions, CircuitBreakerState, HealthCheck, HealthStatus } from './retry';

export { Counter, Gauge, Histogram, MetricsRegistry, createMetricsRegistry, defaultMetrics } from './metrics';
export type { HistogramSnapshot, MetricsSnapshot } from './metrics';

// documented error codes
//
// The comprehensive error code system in ./errors provides unique, documentable
// error codes (NOBULEX_Exxx). The legacy NobulexErrorCode/NobulexError above are
// retained for backward compatibility. Import directly from './errors' for the
// full documented error code system.
export {
  NobulexErrorCode as DocumentedErrorCode,
  NobulexError as DocumentedNobulexError,
  errorDocsUrl,
  formatError,
} from './errors';
export type { NobulexErrorOptions } from './errors';

// ---
export { isDebugEnabled, createDebugLogger, debug } from './debug';
export type { DebugLogger } from './debug';

// deprecation warnings
export { deprecated, wrapDeprecated, resetDeprecationWarnings, getEmittedWarnings } from './deprecation';
export type { DeprecationWarning } from './deprecation';

// exports
export * from './constants';

// core protocol types (merged from @nobulex/core-types)
export * from './core-types';
