import { NativeEventEmitter, NativeModules, Platform } from 'react-native';
import { Event } from './Events/Event';
import { UserProperties } from './UserProperties';
import {
  parseBluxNotification,
  type NotificationClickedHandler,
} from './notifications/BluxNotification';
import type {
  NotificationForegroundWillDisplayEvent,
  NotificationForegroundWillDisplayHandler,
} from './notifications/NotificationForegroundWillDisplayEvent';
import type { NotificationUrlOpenOptions } from './notifications/NotificationUrlOpenOptions';
import type { InAppUrlOpenOptions } from './notifications/InAppUrlOpenOptions';
import {
  parseBluxInApp,
  type InAppClickedHandler,
} from './notifications/BluxInApp';

const LINKING_ERROR =
  `The package '@blux.ai/react-native' doesn't seem to be linked. Make sure: \n\n` +
  Platform.select({ ios: "- You have run 'pod install'\n", default: '' }) +
  '- You rebuilt the app after installing the package\n' +
  '- You are not using Expo Go\n';

const BluxModule = NativeModules.Blux
  ? NativeModules.Blux
  : new Proxy(
      {},
      {
        get() {
          throw new Error(LINKING_ERROR);
        },
      }
    );

export type LogLevel = 'error' | 'verbose';

export type InitializeParams = {
  bluxApplicationId: string;
  bluxAPIKey: string;
  requestPermissionOnLaunch: boolean;
  customDeviceId?: string;
};

export type WebViewMessage = {
  nativeEvent: {
    data: string;
  };
};

/**
 * Custom HTML 인앱 메시지에서 `BluxBridge.triggerAction()` 호출 시 전달되는 이벤트
 */
export type InAppCustomActionEvent = {
  /** 액션 식별자 (예: "share", "navigate") */
  actionId: string;
  /** 액션과 함께 전달된 데이터 */
  data: Record<string, any>;
};

/**
 * Custom HTML 인앱 메시지에서 `BluxBridge.triggerAction()` 호출 시 실행되는 핸들러 타입
 */
export type InAppCustomActionHandler = (event: InAppCustomActionEvent) => void;

const NOTIFICATION_CLICKED_EVENT_KEY = 'notification-clicked';
const NOTIFICATION_FOREGROUND_WILL_DISPLAY_EVENT_KEY =
  'notification-foreground-will-display';
const INAPP_CLICKED_EVENT_KEY = 'inapp-clicked';
const INAPP_CUSTOM_ACTION_EVENT_KEY = 'inapp-custom-action';

class BluxClient {
  static eventEmitter = new NativeEventEmitter(BluxModule);

  private static inAppCustomActionHandlers: InAppCustomActionHandler[] = [];

  static async initialize(params: InitializeParams): Promise<void> {
    await BluxModule.initialize(
      params.bluxApplicationId,
      params.bluxAPIKey,
      params.requestPermissionOnLaunch,
      params.customDeviceId ?? null
    );
  }

  static async setLogLevel(level: LogLevel): Promise<void> {
    await BluxModule.setLogLevel(level);
  }

  static async signIn({ userId }: { userId: string }): Promise<void> {
    await BluxModule.signIn(userId);
  }

  static async signOut(): Promise<void> {
    await BluxModule.signOut();
  }

  static async sendEvent(event: Event | Event[]): Promise<void> {
    await BluxModule.sendRequest(
      (Array.isArray(event) ? event : [event]).map((r) => r.request)
    );
  }

  static async sendEventData(events: object[]): Promise<void> {
    await BluxModule.sendRequest(events);
  }

  static async setUserProperties({
    userProperties,
  }: {
    userProperties: UserProperties;
  }): Promise<void> {
    await BluxModule.setUserProperties(userProperties.toJson());
  }

  static async setCustomUserProperties({
    customUserProperties,
  }: {
    customUserProperties: Record<string, any>;
  }): Promise<void> {
    await BluxModule.setCustomUserProperties(customUserProperties);
  }

  /**
   * 푸시 알림 URL을 어떻게 열지 호스트 앱이 제어합니다.
   *
   * - `internalWebView` (기본값): SDK 내장 WebView로 연다
   * - `externalBrowser`: 외부 브라우저로 연다
   * - `none`: SDK가 자동으로 열지 않는다 ({@link setNotificationClickedHandler}에서 직접 처리)
   *
   * @example
   * ```typescript
   * await BluxClient.setNotificationUrlOpenOptions({ httpUrlOpenTarget: 'externalBrowser' });
   * ```
   */
  static async setNotificationUrlOpenOptions(
    options: NotificationUrlOpenOptions
  ): Promise<void> {
    await BluxModule.setNotificationUrlOpenOptions({
      httpUrlOpenTarget: options.httpUrlOpenTarget ?? 'internalWebView',
    });
  }

  /**
   * 앱이 포그라운드 상태일 때 푸시 알림이 표시되기 직전에 실행될 핸들러를 등록합니다.
   *
   * 핸들러 안에서 `event.display()`를 호출해야 시스템 알림이 실제로 표시됩니다.
   * 호출하지 않으면 시스템 알림은 표시되지 않으니, 인앱 UI로만 처리할 때 유용합니다.
   *
   * @example
   * ```typescript
   * BluxClient.setNotificationForegroundWillDisplayHandler(async (event) => {
   *   await event.display();
   * });
   * ```
   */
  static async setNotificationForegroundWillDisplayHandler(
    handler: NotificationForegroundWillDisplayHandler
  ): Promise<void> {
    this.eventEmitter.removeAllListeners(
      NOTIFICATION_FOREGROUND_WILL_DISPLAY_EVENT_KEY
    );
    this.eventEmitter.addListener(
      NOTIFICATION_FOREGROUND_WILL_DISPLAY_EVENT_KEY,
      (raw) => {
        try {
          const notification = parseBluxNotification(raw?.notification);
          const event: NotificationForegroundWillDisplayEvent = {
            notification,
            display: async () => {
              await BluxModule.displayNotification({
                id: notification.id,
                title: notification.title ?? null,
                body: notification.body,
                url: notification.url ?? null,
                imageUrl: notification.imageUrl ?? null,
                data: notification.data ?? null,
              });
            },
          };
          handler(event);
        } catch (e) {
          console.error('NotificationForegroundWillDisplayHandler error:', e);
        }
      }
    );
    await BluxModule.startNotificationForegroundWillDisplayHandler?.();
  }

  /**
   * 푸시 알림 클릭 시 실행될 핸들러를 등록합니다.
   *
   * 등록 이전에 클릭된 알림이 있으면 등록 직후 즉시 핸들러로 전달됩니다.
   *
   * @example
   * ```typescript
   * BluxClient.setNotificationClickedHandler((notification) => {
   *   if (notification.url) myRouter.go(notification.url);
   * });
   * ```
   */
  static async setNotificationClickedHandler(
    handler: NotificationClickedHandler
  ): Promise<void> {
    this.eventEmitter.removeAllListeners(NOTIFICATION_CLICKED_EVENT_KEY);
    this.eventEmitter.addListener(NOTIFICATION_CLICKED_EVENT_KEY, (raw) => {
      try {
        handler(parseBluxNotification(raw));
      } catch (e) {
        console.error('NotificationClickedHandler error:', e);
      }
    });
    await BluxModule.startNotificationClickedHandler?.();
  }

  /**
   * 인앱 메시지 URL을 어떻게 열지 호스트 앱이 제어합니다.
   *
   * - `internalWebView` (기본값): SDK 내장 WebView로 연다
   * - `externalBrowser`: 외부 브라우저로 연다
   * - `none`: SDK가 자동으로 열지 않는다
   *
   * @example
   * ```typescript
   * await BluxClient.setInAppUrlOpenOptions({ httpUrlOpenTarget: 'externalBrowser' });
   * ```
   */
  static async setInAppUrlOpenOptions(
    options: InAppUrlOpenOptions
  ): Promise<void> {
    await BluxModule.setInAppUrlOpenOptions({
      httpUrlOpenTarget: options.httpUrlOpenTarget ?? 'internalWebView',
    });
  }

  /**
   * 인앱 메시지의 http/https 링크 클릭을 호스트 앱이 직접 처리하도록 콜백을 등록합니다.
   *
   * 등록되어 있으면 SDK는 클릭된 URL을 자동으로 열지 않고 콜백만 호출합니다.
   * (클릭 트래킹과 인앱 dismiss는 정상 수행)
   * 등록되어 있지 않으면 {@link setInAppUrlOpenOptions} 정책이 적용됩니다.
   *
   * Custom HTML 인앱의 `BluxBridge.triggerAction()` 처리에는 영향을 주지 않습니다.
   * Custom HTML에서 호스트 라우팅을 받고 싶다면 {@link addInAppCustomActionHandler}를 사용하세요.
   *
   * @example
   * ```typescript
   * BluxClient.setInAppClickedHandler((inApp) => {
   *   if (inApp.url) myRouter.go(inApp.url);
   * });
   * ```
   */
  static async setInAppClickedHandler(
    handler: InAppClickedHandler
  ): Promise<void> {
    this.eventEmitter.removeAllListeners(INAPP_CLICKED_EVENT_KEY);
    this.eventEmitter.addListener(INAPP_CLICKED_EVENT_KEY, (raw) => {
      try {
        handler(parseBluxInApp(raw));
      } catch (e) {
        console.error('InAppClickedHandler error:', e);
      }
    });
    await BluxModule.startInAppClickedHandler?.();
  }

  /**
   * Custom HTML 인앱 메시지에서 `BluxBridge.triggerAction()` 호출 시 실행될 핸들러를 등록합니다.
   *
   * 여러 핸들러를 등록할 수 있으며 등록 순서대로 실행됩니다.
   * 반환된 unsubscribe 함수를 호출하면 등록한 핸들러만 제거됩니다.
   *
   * @returns 등록한 핸들러를 제거하는 unsubscribe 함수
   *
   * @example
   * ```typescript
   * const unsubscribe = BluxClient.addInAppCustomActionHandler((event) => {
   *   if (event.actionId === 'share') {
   *     // 공유 로직
   *   } else if (event.actionId === 'navigate') {
   *     const { screen, id } = event.data;
   *   }
   * });
   *
   * unsubscribe();
   * ```
   */
  static addInAppCustomActionHandler(
    handler: InAppCustomActionHandler
  ): () => void {
    this.inAppCustomActionHandlers.push(handler);

    this.eventEmitter.removeAllListeners(INAPP_CUSTOM_ACTION_EVENT_KEY);
    this.eventEmitter.addListener(
      INAPP_CUSTOM_ACTION_EVENT_KEY,
      (event: InAppCustomActionEvent) => {
        for (const h of this.inAppCustomActionHandlers) {
          try {
            h(event);
          } catch (e) {
            console.error('InAppCustomActionHandler error:', e);
          }
        }
      }
    );

    BluxModule.startInAppCustomActionHandler?.();

    return () => {
      this.inAppCustomActionHandlers = this.inAppCustomActionHandlers.filter(
        (h) => h !== handler
      );
    };
  }

  /**
   * 현재 표시 중인 인앱 메시지를 프로그래밍 방식으로 닫습니다.
   *
   * Custom HTML 인앱에서 async 핸들러 완료 후 수동으로 닫을 때 사용합니다.
   *
   * @example
   * ```typescript
   * BluxClient.addInAppCustomActionHandler(async (event) => {
   *   if (event.actionId === 'share') {
   *     await Share.share({ message: event.data.url });
   *     BluxClient.dismissInApp();
   *   }
   * });
   *
   * // HTML에서 shouldDismiss: false로 호출
   * // BluxBridge.triggerAction('share', { url: '...' }, false);
   * ```
   */
  static dismissInApp(): void {
    BluxModule.dismissInApp?.();
  }

  static async onMessage(message: WebViewMessage | string): Promise<void> {
    try {
      const parsedMessage = JSON.parse(
        typeof message === 'string' ? message : message.nativeEvent.data
      );
      const { action, payload } = parsedMessage;

      switch (action) {
        case 'initialize':
          if (payload?.bluxApplicationId && payload?.bluxAPIKey) {
            await BluxClient.initialize({
              bluxApplicationId: payload.bluxApplicationId,
              bluxAPIKey: payload.bluxAPIKey,
              requestPermissionOnLaunch:
                payload.requestPermissionOnLaunch ?? true,
              customDeviceId: payload.customDeviceId ?? undefined,
            });
          }
          break;
        case 'signIn':
          if (payload?.userId) {
            await BluxClient.signIn({ userId: payload.userId });
          }
          break;
        case 'signOut':
          await BluxClient.signOut();
          break;
        case 'setUserProperties':
          const userProperties = UserProperties.from(payload);
          if (userProperties) {
            await BluxClient.setUserProperties({ userProperties });
          }
          break;
        case 'setCustomUserProperties':
          if (payload) {
            await BluxClient.setCustomUserProperties({
              customUserProperties: payload,
            });
          }
          break;
        case 'sendEvent':
          if (payload?.requests) {
            await BluxClient.sendEventData(payload.requests);
          }
          break;
        default:
          console.warn(`Unknown action: ${action}`);
      }
    } catch (error) {
      console.error('Error parsing message:', error);
    }
  }
}

export default BluxClient;

export * from './Events';
export * from './UserProperties';
export type {
  BluxNotification,
  NotificationClickedHandler,
} from './notifications/BluxNotification';
export type {
  NotificationForegroundWillDisplayEvent,
  NotificationForegroundWillDisplayHandler,
} from './notifications/NotificationForegroundWillDisplayEvent';
export type { HttpUrlOpenTarget } from './notifications/HttpUrlOpenTarget';
export type { NotificationUrlOpenOptions } from './notifications/NotificationUrlOpenOptions';
export type { InAppUrlOpenOptions } from './notifications/InAppUrlOpenOptions';
export type { BluxInApp, InAppClickedHandler } from './notifications/BluxInApp';
