import { BROWSING_CONTEXT_ID } from '@learnitive/config';
import { EXTERNAL_FILE_CHANGE_SENDER_TAG } from '@learnitive/constants';
import type { EventSenderMetadata } from '@learnitive/types';

export type { ErrorReporter } from '@learnitive/logger';
export { Logger, setErrorReporter, setGlobalLogLevel } from '@learnitive/logger';
export type {
  AllEventListener,
  Brand,
  EventListener,
  EventMessage,
  IfEquals,
  InferType,
  JsonValue,
  JsonValueSnapshotResult,
  Validator,
} from '@learnitive/mini-js-utils';
export {
  assertIsDefined,
  BaseError,
  browserInfo,
  createEmptyArray,
  createJsonValueSnapshot,
  DuoWeakMap,
  deepMerge,
  difference,
  Emitter,
  expectType,
  getLast,
  intersect,
  isAbortError,
  isChrome,
  isDarwin,
  isFirefox,
  isJsonValue,
  isMac,
  isMobile,
  isPlainObject,
  isSafari,
  isUndefined,
  isUndefinedOrNull,
  T,
  weakCache,
  weakCacheDuo,
} from '@learnitive/mini-js-utils';
export { BaseErrorService } from './base-error-service';
export type { BaseServiceContext } from './base-service';
export { BaseService } from './base-service';
export { cx } from './cx';
export { getGithubUrl } from './github-bug-url';
export {
  arrayEqual,
  atomStorage,
  atomWithCompare,
  createAsyncAtom,
} from './jotai';
export { checkWidescreen } from './misc';
export type { AppErrorName } from './throw-app-error';
export {
  createAppError,
  getAppErrorCause,
  handleAppError,
  isAppError,
  throwAppError,
  wrapPromiseInAppErrorHandler,
} from './throw-app-error';
export function getEventSenderMetadata({
  tag,
}: {
  tag?: undefined | string;
}): EventSenderMetadata {
  // The per-browsing-context id lets cross-tab event listeners tell their own
  // tab's events apart from another tab's (e.g. note snapshot throttling).
  return {
    id: BROWSING_CONTEXT_ID,
    tag: tag,
  };
}

/**
 * Where an app event came from, relative to the listening context. The two
 * axes are independent: a storage watcher reports this tab's own writes
 * (`watcher` without `foreignTab`), while another tab's watcher is both.
 * `external` means "not caused by this context's own code path".
 */
export function classifyEventSender(
  sender: EventSenderMetadata,
  selfSenderId: string,
): { external: boolean; foreignTab: boolean; watcher: boolean } {
  const watcher = sender.tag === EXTERNAL_FILE_CHANGE_SENDER_TAG;
  const foreignTab = sender.id !== selfSenderId;
  return { external: watcher || foreignTab, foreignTab, watcher };
}
