/* eslint-disable import/no-default-export -- required for default export*/
import { CommandQueue, CommandType } from "@/lib/common/command-queue";
import * as Setup from "@/lib/common/setup";
import { getIsDebug } from "@/lib/common/utils";
import * as Action from "@/lib/survey/action";
import { checkPageUrl } from "@/lib/survey/no-code-action";
import * as Attribute from "@/lib/user/attribute";
import * as User from "@/lib/user/user";
import { type TConfigInput, type TLegacyConfigInput } from "@/types/config";
import { type TTrackProperties } from "@/types/survey";

const queue = CommandQueue.getInstance();

const setup = async (setupConfig: TConfigInput): Promise<void> => {
  // If the initConfig has a userId or attributes, we need to use the legacy init

  if (
    // @ts-expect-error -- userId and attributes were in the older type
    setupConfig.userId ||
    // @ts-expect-error -- attributes were in the older type
    setupConfig.attributes ||
    // @ts-expect-error -- apiHost was in the older type
    setupConfig.apiHost
  ) {
    const isDebug = getIsDebug();
    if (isDebug) {
      // eslint-disable-next-line no-console -- legacy init
      console.warn("🧱 Formbricks - Warning: Using legacy init");
    }
    await queue.add(Setup.setup, CommandType.Setup, false, {
      ...setupConfig,
      // @ts-expect-error -- apiHost was in the older type
      ...(setupConfig.apiHost && { appUrl: setupConfig.apiHost as string }),
    } as unknown as TConfigInput);
  } else {
    await queue.add(Setup.setup, CommandType.Setup, false, setupConfig);
  }

  // wait for setup to complete
  await queue.wait();

  // Schedule checkPageUrl to run in the next event loop iteration.
  // This ensures that any user actions (like setUserId) called synchronously after setup()
  // will be queued BEFORE the page view actions are processed.
  setTimeout(() => {
    void checkPageUrl();
  }, 0);
};

const setUserId = async (userId: string): Promise<void> => {
  await queue.add(User.setUserId, CommandType.UserAction, true, userId);
};

const setEmail = async (email: string): Promise<void> => {
  await queue.add(Attribute.setAttributes, CommandType.UserAction, true, { email });
};

const setAttribute = async (key: string, value: string): Promise<void> => {
  await queue.add(Attribute.setAttributes, CommandType.UserAction, true, { [key]: value });
};

const setAttributes = async (attributes: Record<string, string>): Promise<void> => {
  await queue.add(Attribute.setAttributes, CommandType.UserAction, true, attributes);
};

const setLanguage = async (language: string): Promise<void> => {
  await queue.add(Attribute.setAttributes, CommandType.UserAction, true, { language });
};

const logout = async (): Promise<void> => {
  await queue.add(User.logout, CommandType.GeneralAction);
};

/**
 * @param code - The code of the action to track
 * @param properties - Optional properties to set, like the hidden fields (deprecated, hidden fields will be removed in a future version)
 */
const track = async (code: string, properties?: TTrackProperties): Promise<void> => {
  await queue.add(Action.trackCodeAction, CommandType.GeneralAction, true, code, properties);
};

const registerRouteChange = async (): Promise<void> => {
  await queue.add(checkPageUrl, CommandType.GeneralAction);
};

/**
 * Set the CSP nonce for inline styles
 * @param nonce - The CSP nonce value (without 'nonce-' prefix), or undefined to clear
 */
const setNonce = (nonce: string | undefined): void => {
  // Store nonce on window for access when surveys package loads
  globalThis.window.__formbricksNonce = nonce;

  // Set nonce in surveys package if it's already loaded
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- Runtime check for surveys package availability
  globalThis.window.formbricksSurveys?.setNonce?.(nonce);
};

const formbricks = {
  /** @deprecated Use setup() instead. This method will be removed in a future version */
  init: (initConfig: TLegacyConfigInput) => setup(initConfig as unknown as TConfigInput),
  setup,
  setEmail,
  setAttribute,
  setAttributes,
  setLanguage,
  setUserId,
  track,
  logout,
  registerRouteChange,
  setNonce,
};

// Explicitly assign to globalThis so the wrapper SDK (@formbricks/js) can
// find us even when the UMD environment detection is fooled by a leaked
// `exports` or `module` global on the page (e.g. from another UMD bundle,
// a tag manager, or a browser extension).  This runs inside the UMD factory,
// so it executes regardless of which branch the wrapper picks.
(globalThis as unknown as Record<string, unknown>).formbricks = formbricks;

type TFormbricks = typeof formbricks;
export type { TFormbricks };
export default formbricks;
