import { Platform, NativeModules, AppState, Vibration } from 'react-native';
import { pushService } from './pushService';
import * as Notifications from 'expo-notifications';
import { getNotificationPreferencesSync } from './notificationPreferences';
import { vibrateOnMessage } from '../platform/messageVibrationService';

type JPushNotificationMessage = {
  messageID: string;
  title: string;
  content: string;
  extras: Record<string, string>;
  notificationType?: string;
  notificationEventType?: 'notificationArrived' | 'notificationOpened';
};

type JPushCallback = (message: JPushNotificationMessage) => void;

let JPush: typeof import('jpush-react-native').default | null = null;
let jpushAppKey = '';
let jpushChannel = 'developer-default';

try {
  const nativeModule = NativeModules.JPushModule;
  if (nativeModule) {
    JPush = require('jpush-react-native').default;
    const Constants = require('expo-constants').default;
    const extra = Constants?.expoConfig?.extra || Constants?.extra || {};
    jpushAppKey = extra.jpushAppKey || '';
    jpushChannel = extra.jpushChannel || 'withyou-default';
  } else {
    console.warn('[JPush] native module not linked, skipping');
  }
} catch {
  console.warn('[JPush] jpush-react-native not available');
}

function getAuthStore() {
  return require('@/stores').useAuthStore;
}

class JPushService {
  private registrationID: string | null = null;
  private isInitialized = false;
  private initPromise: Promise<boolean> | null = null;
  private notificationCallback: JPushCallback | null = null;
  private connectResolved = false;
  private listenersRegistered = false;

  async initialize(): Promise<boolean> {
    if (this.isInitialized) return true;
    if (Platform.OS === 'web') return false;
    if (!JPush) return false;
    if (this.initPromise) return this.initPromise;

    this.initPromise = this._doInit();
    return this.initPromise;
  }

  private _registerListeners(): void {
    if (this.listenersRegistered) return;
    this.listenersRegistered = true;

    JPush!.addConnectEventListener((result: { connectEnable: boolean }) => {
      console.log('[JPush] connect event:', result.connectEnable);
      if (result.connectEnable && !this.connectResolved) {
        this.connectResolved = true;
        this._onConnected();
      }
    });

    JPush!.addNotificationListener((result: any) => {
      console.log('[JPush] notification:', result);
      if (result.notificationEventType === 'notificationArrived') {
        const prefs = getNotificationPreferencesSync();
        if (prefs.vibrationEnabled && AppState.currentState === 'active') {
          vibrateOnMessage('notification').catch(() => {});
        }
      }
      if (this.notificationCallback) {
        this.notificationCallback({
          messageID: result.messageID || '',
          title: result.title || '',
          content: result.content || '',
          extras: result.extras || {},
          notificationType: result.extras?.notification_type,
          notificationEventType: result.notificationEventType,
        });
      }
    });

    JPush!.addCustomMessageListener((result: any) => {
      console.log('[JPush] custom message:', result);
    });

    JPush!.addLocalNotificationListener((result: any) => {
      console.log('[JPush] local notification:', result);
    });

    JPush!.addTagAliasListener((result: any) => {
      console.log('[JPush] tag/alias result:', result);
    });

    JPush!.addCommandEventListener((result: any) => {
      console.log('[JPush] command result:', result);
    });
  }

  private async _doInit(): Promise<boolean> {
    try {
      JPush!.setLoggerEnable(true);

      // Register listeners only once (idempotent guard)
      this._registerListeners();

      // 退后台保持极光长连接，确保推送实时到达
      this._setBackgroundKeepLongConn(true);

      // 初始化 SDK
      JPush!.init({
        appKey: jpushAppKey,
        channel: jpushChannel,
        production: !__DEV__,
      });

      if (Platform.OS === 'android') {
        this._setupAndroidNotificationChannel();
      }

      console.log('[JPush] init called, waiting for registration...');

      // 轮询获取 RegistrationID，间隔逐渐增大
      const regID = await this._pollRegistrationID();
      if (regID) {
        this.registrationID = regID;
        this.isInitialized = true;
        console.log('[JPush] initialized with registrationID:', regID);
        await this.registerDeviceToServer();
        return true;
      }

      // 即使没拿到 registrationID，也标记为已初始化
      // connect 事件回来后会自动获取
      this.isInitialized = true;
      console.warn('[JPush] init done but no registrationID yet, will retry on connect');
      return true;
    } catch (error) {
      console.error('[JPush] initialization failed:', error);
      this.initPromise = null;
      return false;
    }
  }

  private _onConnected(): void {
    if (this.registrationID) return;
    console.log('[JPush] connected, fetching registrationID...');
    this._fetchRegistrationID();
  }

  private _setupAndroidNotificationChannel(): void {
    if (!JPush) return;
    try {
      (JPush as any).setChannelAndSound({
        channel: '推送通知',
        channelId: 'jpush_notification',
      });
      console.log('[JPush] Android notification channel configured');
    } catch (error) {
      console.warn('[JPush] setChannelAndSound failed:', error);
    }

    // Ensure notification channel has sound & vibration enabled via expo-notifications
    try {
      Notifications.setNotificationChannelAsync('jpush_notification', {
        name: '推送通知',
        importance: Notifications.AndroidImportance.HIGH,
        vibrationPattern: [0, 250, 250, 250],
        enableLights: true,
        showBadge: true,
      });
    } catch (error) {
      console.warn('[JPush] expo-notifications channel setup failed:', error);
    }
  }

  /**
   * 设置退后台是否保持极光长连接
   * @see https://docs.jiguang.cn/jpush/client/Android/android_api#退后台是否保持极光长连接-api
   */
  private _setBackgroundKeepLongConn(keep: boolean): void {
    if (!JPush) return;
    try {
      if (Platform.OS === 'android') {
        const jpushModule = NativeModules.JPushModule;
        if (jpushModule?.setKeepLongConnInBackground) {
          jpushModule.setKeepLongConnInBackground(keep);
          console.log('[JPush] setKeepLongConnInBackground:', keep);
        } else {
          console.warn('[JPush] setKeepLongConnInBackground not available in native module');
        }
      } else if (Platform.OS === 'ios') {
        JPush.setBackgroundEnable(keep);
        console.log('[JPush] setBackgroundEnable:', keep);
      }
    } catch (error) {
      console.warn('[JPush] setBackgroundKeepLongConn failed:', error);
    }
  }

  private _fetchRegistrationID(): void {
    if (!JPush) return;
    JPush.getRegistrationID((result: any) => {
      console.log('[JPush] getRegistrationID result:', JSON.stringify(result));
      const regID = result?.registerID || result?.registrationID;
      if (regID) {
        this.registrationID = regID;
        this.isInitialized = true;
        console.log('[JPush] got registrationID:', regID);
        this.registerDeviceToServer();
      } else {
        console.warn('[JPush] getRegistrationID returned empty');
      }
    });
  }

  private _pollRegistrationID(): Promise<string | null> {
    return new Promise((resolve) => {
      // 轮询策略: 2s, 4s, 6s, 8s, 10s (总计 30s)
      const delays = [2000, 4000, 6000, 8000, 10000];
      let attempt = 0;

      const tryFetch = () => {
        if (!JPush) {
          resolve(null);
          return;
        }

        JPush.getRegistrationID((result: any) => {
          const regID = result?.registerID || result?.registrationID;
          console.log(`[JPush] poll attempt ${attempt + 1}, regID:`, regID || '(empty)');

          if (regID) {
            resolve(regID);
            return;
          }

          attempt++;
          if (attempt < delays.length) {
            setTimeout(tryFetch, delays[attempt] - (attempt > 0 ? delays[attempt - 1] : 0));
          } else {
            resolve(null);
          }
        });
      };

      setTimeout(tryFetch, delays[0]);
    });
  }

  async registerDeviceToServer(): Promise<void> {
    if (!this.registrationID) return;

    const useAuthStore = getAuthStore();
    const { isAuthenticated } = useAuthStore.getState();
    if (!isAuthenticated) return;

    try {
      const deviceType = Platform.OS === 'ios' ? 'ios' : 'android';
      const deviceID = await this.getDeviceID();

      await pushService.registerDevice({
        device_id: deviceID,
        device_type: deviceType as 'ios' | 'android',
        push_token: this.registrationID,
        device_name: `${Platform.OS === 'ios' ? 'iOS' : 'Android'} Device`,
      });

      console.log('[JPush] device registered to server');
    } catch (error) {
      console.error('[JPush] failed to register device to server:', error);
    }
  }

  private async getDeviceID(): Promise<string> {
    try {
      const AsyncStorage = require('@react-native-async-storage/async-storage').default;
      const DEVICE_ID_KEY = 'withyou_device_id';
      let deviceId = await AsyncStorage.getItem(DEVICE_ID_KEY);
      if (deviceId) return deviceId;
      const timestamp = Date.now().toString(36);
      const random = Math.random().toString(36).substring(2, 8);
      deviceId = `${Platform.OS}_${timestamp}_${random}`;
      await AsyncStorage.setItem(DEVICE_ID_KEY, deviceId);
      return deviceId;
    } catch {
      return `${Platform.OS}_${Date.now()}`;
    }
  }

  onNotification(callback: JPushCallback): void {
    this.notificationCallback = callback;
  }

  setTags(tags: string[]): void {
    if (!JPush || !this.isInitialized) return;
    try {
      JPush.addTags({ sequence: Date.now(), tags });
    } catch (error) {
      console.error('[JPush] setTags failed:', error);
    }
  }

  setAlias(alias: string): void {
    if (!JPush || !this.isInitialized) return;
    try {
      JPush.setAlias({ sequence: Date.now(), alias });
    } catch (error) {
      console.error('[JPush] setAlias failed:', error);
    }
  }

  async setAliasForUser(userID: string): Promise<void> {
    this.setAlias(userID);
  }

  deleteAlias(): void {
    if (!JPush || !this.isInitialized) return;
    try {
      JPush.deleteAlias({ sequence: Date.now() });
    } catch (error) {
      console.error('[JPush] deleteAlias failed:', error);
    }
  }

  getRegistrationIDValue(): string | null {
    return this.registrationID;
  }

  isJPushAvailable(): boolean {
    return this.isInitialized && this.registrationID !== null;
  }

  addLocalNotification(params: {
    messageID: string;
    title: string;
    content: string;
    extras: Record<string, string>;
  }): void {
    if (!JPush || !this.isInitialized) return;
    try {
      if (Platform.OS === 'android') {
        const localParams = {
          ...params,
          priority: 1,
        };
        JPush.addLocalNotification(localParams);
      } else {
        JPush.addLocalNotification(params);
      }

      // JPush local notification does not support sound/vibration directly.
      // Trigger them manually based on user preferences.
      const prefs = getNotificationPreferencesSync();
      if (prefs.soundEnabled) {
        // Play system default notification sound via a silent expo-notification
        Notifications.scheduleNotificationAsync({
          content: { title: '', body: '', sound: true, vibrate: [] },
          trigger: null,
        }).catch(() => {});
      }
      if (prefs.vibrationEnabled) {
        // Use React Native Vibration API for device-level vibration
        Vibration.vibrate([0, 250, 250, 250]);
      }
    } catch (error) {
      console.error('[JPush] addLocalNotification failed:', error);
    }
  }

  clearAllNotifications(): void {
    if (!JPush || !this.isInitialized) return;
    try {
      JPush.clearAllNotifications();
    } catch (error) {
      console.error('[JPush] clearAllNotifications failed:', error);
    }
  }

  setBadge(badge: number): void {
    if (!JPush || !this.isInitialized || Platform.OS !== 'ios') return;
    try {
      JPush.setBadge({ badge, appBadge: badge });
    } catch (error) {
      console.error('[JPush] setBadge failed:', error);
    }
  }

  /**
   * 请求通知权限（使用 expo-notifications，统一处理 iOS/Android）
   */
  async requestNotificationPermission(): Promise<boolean> {
    if (Platform.OS === 'web') return false;

    try {
      const { status } = await Notifications.requestPermissionsAsync();
      return status === 'granted';
    } catch (error) {
      console.warn('[JPush] request notification permission failed:', error);
      return false;
    }
  }

  /**
   * 检查通知权限是否已开启
   */
  async checkNotificationPermission(): Promise<boolean> {
    if (Platform.OS === 'web') return false;

    try {
      const { status } = await Notifications.getPermissionsAsync();
      return status === 'granted';
    } catch {
      return false;
    }
  }

  cleanup(): void {
    this.isInitialized = false;
    this.registrationID = null;
    this.notificationCallback = null;
    this.initPromise = null;
    this.connectResolved = false;
  }
}

export const jpushService = new JPushService();
export default jpushService;