/**
 * Correlation ID Module
 *
 * Provides request tracing across services with unique correlation IDs.
 * Format: mwd-{timestamp}-{random}
 */

type AsyncLocalStorageType<T> = {
  getStore(): T | undefined;
  run<R>(store: T, callback: () => R): R;
};

let AsyncLocalStorage: (new <T>() => AsyncLocalStorageType<T>) | null = null;

// process.env.NEXT_RUNTIME check enables webpack to tree-shake this block
// when compiling for the Edge runtime (middleware), preventing bundling failure
// because async_hooks is a Node.js-only module not available in Edge runtime.
if (process.env.NEXT_RUNTIME !== "edge" && typeof window === "undefined") {
  try {
    // eslint-disable-next-line @typescript-eslint/no-require-imports
    AsyncLocalStorage = require("async_hooks").AsyncLocalStorage;
  } catch {
    AsyncLocalStorage = null;
  }
}

export interface CorrelationContext {
  correlationId: string;
  requestId: string;
  startTime: number;
  userId?: string;
  organizationId?: string;
  sessionId?: string;
}

export const CORRELATION_ID_HEADER = "x-correlation-id";
export const REQUEST_ID_HEADER = "x-request-id";

const CORRELATION_ID_PREFIX = "mwd";
const REQUEST_ID_PREFIX = "req";

const correlationStorage: AsyncLocalStorageType<CorrelationContext> | null =
  AsyncLocalStorage ? new AsyncLocalStorage<CorrelationContext>() : null;

function randomString(length: number): string {
  const chars = "abcdefghijklmnopqrstuvwxyz0123456789";
  let result = "";
  for (let i = 0; i < length; i++) {
    result += chars.charAt(Math.floor(Math.random() * chars.length));
  }
  return result;
}

export function generateCorrelationId(): string {
  const timestamp = Date.now();
  const random = randomString(8);
  return `${CORRELATION_ID_PREFIX}-${timestamp}-${random}`;
}

export function generateRequestId(): string {
  const timestamp = Date.now();
  const random = randomString(8);
  return `${REQUEST_ID_PREFIX}-${timestamp}-${random}`;
}

export function isValidCorrelationId(id: string): boolean {
  if (!id || typeof id !== "string") return false;
  return /^mwd-\d{13}-[a-z0-9]{8}$/.test(id);
}

export function isValidRequestId(id: string): boolean {
  if (!id || typeof id !== "string") return false;
  return /^req-\d{13}-[a-z0-9]{8}$/.test(id);
}

export function getCorrelationContext(): CorrelationContext | undefined {
  return correlationStorage?.getStore();
}

export function getCurrentCorrelationId(): string | undefined {
  return correlationStorage?.getStore()?.correlationId;
}

export function getCurrentRequestId(): string | undefined {
  return correlationStorage?.getStore()?.requestId;
}

export function runWithCorrelation<T>(
  context: CorrelationContext,
  callback: () => T,
): T {
  if (!correlationStorage) return callback();
  return correlationStorage.run(context, callback);
}

export function getCorrelationIdFromHeaders(
  headers: Headers | Record<string, string | string[] | undefined>,
): string {
  let existingId: string | undefined;
  if (headers instanceof Headers) {
    existingId = headers.get(CORRELATION_ID_HEADER) || undefined;
  } else {
    const value = headers[CORRELATION_ID_HEADER];
    existingId = Array.isArray(value) ? value[0] : value;
  }
  if (existingId && isValidCorrelationId(existingId)) return existingId;
  return generateCorrelationId();
}

export function getCorrelationHeaders(
  additionalHeaders: Record<string, string> = {},
): Record<string, string> {
  const context = getCorrelationContext();
  return {
    ...additionalHeaders,
    ...(context?.correlationId
      ? { [CORRELATION_ID_HEADER]: context.correlationId }
      : {}),
    ...(context?.requestId ? { [REQUEST_ID_HEADER]: context.requestId } : {}),
  };
}

export function getCorrelationMetadata(): Record<string, string | undefined> {
  const context = getCorrelationContext();
  if (!context) return {};
  return {
    correlationId: context.correlationId,
    requestId: context.requestId,
    userId: context.userId,
    organizationId: context.organizationId,
    sessionId: context.sessionId,
  };
}

export function getRequestDuration(): number | undefined {
  const context = getCorrelationContext();
  if (!context) return undefined;
  return Date.now() - context.startTime;
}
