import type { IOneSignalOneSignal } from "react-onesignal";

export interface OneSignalUserTags {
  user_id?: string | number;
  channel_id?: string;
  user_type?: string;
}

let _initialized = false;
let _initializedAppId: string | null = null;
let _sdk: IOneSignalOneSignal | null = null;

function isClient(): boolean {
  return typeof window !== "undefined";
}

async function getSDK(): Promise<IOneSignalOneSignal> {
  if (_sdk) return _sdk;
  const mod = await import("react-onesignal");
  _sdk = mod.default as unknown as IOneSignalOneSignal;
  return _sdk;
}

export async function initOneSignal(appId: string): Promise<void> {
  if (!isClient() || !appId) return;
  if (_initialized && _initializedAppId === appId) return;

  const OneSignal = await getSDK();

  await OneSignal.init({
    appId,
    autoResubscribe: true,
    serviceWorkerPath: "/OneSignalSDKWorker.js",
    // Allow initialization on localhost during development.
    // OneSignal restricts usage to the registered domain; this bypasses
    // that check only when running locally (never sent to production).
    allowLocalhostAsSecureOrigin: process.env.NODE_ENV === "development",
    promptOptions: {
      slidedown: {
        prompts: [
          {
            type: "push" as const,
            autoPrompt: false,
            delay: { pageViews: 1, timeDelay: 5 },
          },
        ],
      },
    },
    // Disable the built-in bell widget — we rely on custom permission prompts
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    notifyButton: { enable: false } as any,
  });

  _initialized = true;
  _initializedAppId = appId;
}

export async function requestNotificationPermission(): Promise<boolean> {
  if (!isClient() || !_initialized) return false;
  try {
    const OneSignal = await getSDK();
    return await OneSignal.Notifications.requestPermission();
  } catch {
    return false;
  }
}

export async function getPlayerId(): Promise<string | null> {
  if (!isClient() || !_initialized) return null;
  const OneSignal = await getSDK();
  return OneSignal.User.PushSubscription.id ?? null;
}

export async function loginOneSignalUser(
  externalId: string,
  appId?: string,
  restKey?: string,
): Promise<void> {
  if (!isClient() || !_initialized || !externalId) return;
  const OneSignal = await getSDK();

  try {
    await OneSignal.login(externalId);
  } catch (err) {
    // OneSignal user-2: external_id is already claimed by a different onesignal_id
    // (happens when the user logs in on a new device / after clearing browser storage).
    // Resolve server-side: delete the old user that owns the alias, then retry.
    const isConflict =
      err instanceof Error
        ? /user-2|claimed|conflict/i.test(err.message)
        : true; // unknown error shape — attempt cleanup anyway

    if (isConflict && appId && restKey) {
      try {
        const res = await fetch("/api/onesignal/resolve-identity", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ appId, restKey, externalId }),
        });
        if (res.ok) {
          // Freed the alias — retry login on this device
          await OneSignal.login(externalId);
          return;
        }
      } catch {
        // Server-side cleanup failed — fall through and log original error
      }
    }

    console.error("[OneSignal] login failed for externalId:", externalId, err);
  }
}

export async function logoutOneSignal(): Promise<void> {
  if (!isClient() || !_initialized) return;
  const OneSignal = await getSDK();
  await OneSignal.logout();
}

export async function setUserTags(tags: OneSignalUserTags): Promise<void> {
  if (!isClient() || !_initialized) return;
  const OneSignal = await getSDK();
  const normalized = Object.fromEntries(
    Object.entries(tags)
      .filter(([, v]) => v !== undefined && v !== null && String(v) !== "")
      .map(([k, v]) => [k, String(v)]),
  );
  if (Object.keys(normalized).length > 0) {
    OneSignal.User.addTags(normalized);
  }
}

export async function removeUserTags(
  keys: (keyof OneSignalUserTags)[],
): Promise<void> {
  if (!isClient() || !_initialized) return;
  const OneSignal = await getSDK();
  OneSignal.User.removeTags(keys as string[]);
}

export function isOneSignalInitialized(): boolean {
  return _initialized;
}

const PLAYER_ID_KEY = "doliplay_onesignal_player_id";

export function storePlayerId(id: string): void {
  if (!isClient()) return;
  localStorage.setItem(PLAYER_ID_KEY, id);
}

export function getStoredPlayerId(): string | null {
  if (!isClient()) return null;
  return localStorage.getItem(PLAYER_ID_KEY);
}

export function clearStoredPlayerId(): void {
  if (!isClient()) return;
  localStorage.removeItem(PLAYER_ID_KEY);
}
