import {
  fetchWebPushConfig,
  registerWebPushSubscription,
  unregisterWebPushSubscription,
  type WebPushSubscriptionPayload,
} from './notificationApi';
import { getOrCreateClientId } from './clientIdentity';
import { getSavedNotificationDeviceId } from './notificationIdentity';

const WEB_PUSH_METADATA_STORAGE_KEY = 'work-server.web-push.registration-meta.v1';

type WebPushRegistrationMetadata = {
  deviceId: string;
  clientId: string;
  userAgent: string;
  appOrigin: string;
  appDomain: string;
  enabled: boolean;
  updatedAt: string;
};

function getCurrentAppOrigin() {
  if (typeof window === 'undefined') {
    return '';
  }

  return window.location.origin;
}

function getCurrentAppDomain() {
  if (typeof window === 'undefined') {
    return '';
  }

  return window.location.hostname;
}

function buildWebPushRegistrationMetadata(enabled: boolean): WebPushRegistrationMetadata {
  return {
    deviceId: getSavedNotificationDeviceId().trim(),
    clientId: getOrCreateClientId().trim(),
    userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : '',
    appOrigin: getCurrentAppOrigin().trim(),
    appDomain: getCurrentAppDomain().trim(),
    enabled,
    updatedAt: new Date().toISOString(),
  };
}

function storeWebPushRegistrationMetadata(metadata: WebPushRegistrationMetadata | null) {
  if (typeof window === 'undefined') {
    return;
  }

  try {
    if (metadata) {
      window.localStorage.setItem(WEB_PUSH_METADATA_STORAGE_KEY, JSON.stringify(metadata));
      return;
    }

    window.localStorage.removeItem(WEB_PUSH_METADATA_STORAGE_KEY);
  } catch {
    // Ignore storage failures in restricted runtimes.
  }
}

async function syncWebPushRegistrationMetadataWithServiceWorker(
  registration: ServiceWorkerRegistration | null,
  metadata: WebPushRegistrationMetadata | null,
) {
  if (!registration) {
    return;
  }

  const target =
    registration.active ??
    registration.waiting ??
    registration.installing ??
    (typeof navigator !== 'undefined' && 'serviceWorker' in navigator ? navigator.serviceWorker.controller : null);

  if (!target) {
    return;
  }

  target.postMessage({
    type: metadata ? 'WEB_PUSH_SYNC_METADATA' : 'WEB_PUSH_CLEAR_METADATA',
    payload: metadata,
  });
}

export function urlBase64ToUint8Array(base64String: string) {
  const padding = '='.repeat((4 - (base64String.length % 4)) % 4);
  const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/');
  const rawData = window.atob(base64);
  const outputArray = new Uint8Array(rawData.length);

  for (let index = 0; index < rawData.length; index += 1) {
    outputArray[index] = rawData.charCodeAt(index);
  }

  return outputArray;
}

function isSamePushApplicationServerKey(leftKey: ArrayBuffer | null, rightKey: Uint8Array) {
  if (!leftKey) {
    return false;
  }

  const leftBytes = new Uint8Array(leftKey);

  if (leftBytes.byteLength !== rightKey.byteLength) {
    return false;
  }

  for (let index = 0; index < leftBytes.byteLength; index += 1) {
    if (leftBytes[index] !== rightKey[index]) {
      return false;
    }
  }

  return true;
}

export function serializePushSubscription(subscription: PushSubscription): WebPushSubscriptionPayload {
  const json = subscription.toJSON();

  return {
    endpoint: subscription.endpoint,
    expirationTime: subscription.expirationTime,
    keys: {
      p256dh: json.keys?.p256dh ?? '',
      auth: json.keys?.auth ?? '',
    },
  };
}

export async function ensureWebPushSubscriptionRegistered(
  registration: ServiceWorkerRegistration,
  options?: { deviceId?: string },
) {
  const config = await fetchWebPushConfig();

  if (!config.enabled || !config.publicKey) {
    throw new Error('서버 Web Push 설정이 비어 있습니다.');
  }

  const expectedApplicationServerKey = urlBase64ToUint8Array(config.publicKey);
  let subscription = await registration.pushManager.getSubscription();

  if (
    subscription &&
    !isSamePushApplicationServerKey(subscription.options.applicationServerKey, expectedApplicationServerKey)
  ) {
    await unregisterWebPushSubscription(subscription.endpoint).catch(() => undefined);
    await subscription.unsubscribe().catch(() => undefined);
    subscription = null;
  }

  if (!subscription) {
    subscription = await registration.pushManager.subscribe({
      userVisibleOnly: true,
      applicationServerKey: expectedApplicationServerKey,
    });
  }

  await registerWebPushSubscription(serializePushSubscription(subscription), options?.deviceId ?? getSavedNotificationDeviceId());
  const metadata = buildWebPushRegistrationMetadata(true);
  storeWebPushRegistrationMetadata(metadata);
  await syncWebPushRegistrationMetadataWithServiceWorker(registration, metadata);

  return subscription;
}

export async function syncExistingWebPushSubscriptionRegistration(
  registration: ServiceWorkerRegistration,
  options?: { deviceId?: string },
) {
  const config = await fetchWebPushConfig();

  if (!config.enabled || !config.publicKey || typeof Notification === 'undefined' || Notification.permission !== 'granted') {
    return null;
  }

  const subscription = await registration.pushManager.getSubscription();

  if (!subscription) {
    return null;
  }

  await registerWebPushSubscription(serializePushSubscription(subscription), options?.deviceId ?? getSavedNotificationDeviceId());
  const metadata = buildWebPushRegistrationMetadata(true);
  storeWebPushRegistrationMetadata(metadata);
  await syncWebPushRegistrationMetadataWithServiceWorker(registration, metadata);

  return subscription;
}

export async function clearWebPushSubscriptionRegistration(registration: ServiceWorkerRegistration) {
  const subscription = await registration.pushManager.getSubscription();

  if (subscription) {
    await unregisterWebPushSubscription(subscription.endpoint);
    await subscription.unsubscribe();
  }

  storeWebPushRegistrationMetadata(null);
  await syncWebPushRegistrationMetadataWithServiceWorker(registration, null);
}
