type BrowserProfileValue = string | number | boolean | null | undefined;

export type IBrowserProfileDetails = Record<string, BrowserProfileValue>;

export interface IBrowserProfileEvent {
  readonly scope: string;
  readonly label: string;
  readonly durationMs?: number;
  readonly timestamp: string;
  readonly details?: IBrowserProfileDetails;
}

type BrowserProfileWindow = Window & {
  __mxProfileEvents?: IBrowserProfileEvent[];
  __mxProfileSeq?: number;
};

function getWindow(): BrowserProfileWindow | null {
  if (typeof window === 'undefined') {
    return null;
  }
  return window as BrowserProfileWindow;
}

function readProfileSetting(): string | null {
  const currentWindow = getWindow();
  if (!currentWindow) {
    return null;
  }
  const search = typeof currentWindow.location?.search === 'string'
    ? currentWindow.location.search
    : '';
  const value = new URLSearchParams(search).get('mxProfile');
  return typeof value === 'string' && value.trim().length > 0 ? value.trim().toLowerCase() : null;
}

function formatDetails(details?: IBrowserProfileDetails): string {
  if (!details) {
    return '';
  }
  const entries = Object.entries(details)
    .filter(([, value]) => value !== undefined)
    .map(([key, value]) => `${key}=${String(value)}`);
  return entries.length > 0 ? ` ${entries.join(' ')}` : '';
}

function appendBrowserProfileEvent(event: IBrowserProfileEvent): void {
  const currentWindow = getWindow();
  if (!currentWindow) {
    return;
  }
  if (!Array.isArray(currentWindow.__mxProfileEvents)) {
    currentWindow.__mxProfileEvents = [];
  }
  currentWindow.__mxProfileEvents.push(event);
}

function nextProfileSequence(): number {
  const currentWindow = getWindow();
  if (!currentWindow) {
    return 0;
  }
  currentWindow.__mxProfileSeq = (currentWindow.__mxProfileSeq ?? 0) + 1;
  return currentWindow.__mxProfileSeq;
}

function nowMs(): number {
  if (typeof performance !== 'undefined' && typeof performance.now === 'function') {
    return performance.now();
  }
  return Date.now();
}

export function isBrowserProfileEnabled(scope: string): boolean {
  const setting = readProfileSetting();
  if (!setting) {
    return false;
  }
  if (setting === '1' || setting === 'true' || setting === 'all' || setting === '*') {
    return true;
  }
  return setting
    .split(',')
    .map((part) => part.trim())
    .filter((part) => part.length > 0)
    .includes(scope.trim().toLowerCase());
}

export function logBrowserProfile(
  scope: string,
  label: string,
  details?: IBrowserProfileDetails,
): void {
  if (!isBrowserProfileEnabled(scope)) {
    return;
  }
  console.info(`[MxProfile:${scope}] ${label}${formatDetails(details)}`);
  appendBrowserProfileEvent({
    scope,
    label,
    details,
    timestamp: new Date().toISOString(),
  });
}

export function createBrowserProfileSpan(
  scope: string,
  label: string,
  details?: IBrowserProfileDetails,
): (finalDetails?: IBrowserProfileDetails) => void {
  if (!isBrowserProfileEnabled(scope)) {
    return () => {};
  }

  const startTime = nowMs();
  const sequence = nextProfileSequence();
  const startMark = `mx-profile:${scope}:${label}:start:${sequence}`;
  const endMark = `mx-profile:${scope}:${label}:end:${sequence}`;
  if (typeof performance !== 'undefined' && typeof performance.mark === 'function') {
    performance.mark(startMark);
  }
  if (details) {
    logBrowserProfile(scope, `${label}:start`, details);
  } else {
    logBrowserProfile(scope, `${label}:start`);
  }

  return (finalDetails?: IBrowserProfileDetails) => {
    const durationMs = nowMs() - startTime;
    if (typeof performance !== 'undefined' && typeof performance.mark === 'function') {
      performance.mark(endMark);
    }
    if (
      typeof performance !== 'undefined'
      && typeof performance.measure === 'function'
    ) {
      performance.measure(`mx-profile:${scope}:${label}`, startMark, endMark);
    }

    const mergedDetails = finalDetails
      ? { ...(details ?? {}), ...finalDetails }
      : details;

    console.info(
      `[MxProfile:${scope}] ${label} ${durationMs.toFixed(1)}ms${formatDetails(mergedDetails)}`,
    );
    appendBrowserProfileEvent({
      scope,
      label,
      durationMs,
      details: mergedDetails,
      timestamp: new Date().toISOString(),
    });
  };
}

export async function profileBrowserSpan<T>(
  scope: string,
  label: string,
  fn: () => Promise<T>,
  details?: IBrowserProfileDetails,
): Promise<T> {
  const finish = createBrowserProfileSpan(scope, label, details);
  try {
    const result = await fn();
    finish();
    return result;
  } catch (error) {
    finish({
      error: error instanceof Error ? error.message : String(error),
    });
    throw error;
  }
}
