import 'package:flutter/foundation.dart';
import 'package:mypoint_flutter_app/core/utils/extensions/string_extension.dart';
import 'package:universal_html/html.dart' as html;
import 'package:universal_html/js_util.dart';

import 'js_interop_stub.dart'
    if (dart.library.html) 'js_interop_web.dart' as js_interop;

/// X-App-SDK Service for Flutter Web
/// Provides integration with x-app-sdk module and exposes supported APIs to Dart
class XAppSDKService {
  static final XAppSDKService _instance = XAppSDKService._internal();
  factory XAppSDKService() => _instance;
  XAppSDKService._internal();

  bool _isInitialized = false;
  dynamic _sdkModule;
  String? _cachedToken;
  String? _lastError;
  final Set<dynamic> _listenerDisposers = <dynamic>{};
  bool _browserMode = false;

  static bool isBrowserMode() => _instance._browserMode;

  static const Map<String, String> _infoKeyPropertyMap = <String, String>{
    'user': 'USER',
    'userid': 'USER',
    'user_id': 'USER',
  };

  /// Check if SDK is available and initialized
  bool get isInitialized => _isInitialized;

  /// Get cached token (if any)
  String? get cachedToken => _cachedToken;

  /// Get last error message
  String? get lastError => _lastError;

  /// Initialize the SDK service
  Future<void> initialize() async {
    if (!kIsWeb) {
      debugPrint('⚠️ XAppSDKService: initialize() called on non-web platform');
      return;
    }
    if (_isInitialized && _sdkModule != null) {
      return;
    }

    try {
      debugPrint('🔍 XAppSDKService: Initializing...');
      final module = await _loadSdkModule();
      if (module == null) {
        _lastError = 'x-app-sdk module could not be loaded';
        debugPrint('❌ XAppSDKService: $_lastError');
        return;
      }
      _sdkModule = module;
      _isInitialized = true;
      _lastError = null;
      debugPrint('✅ XAppSDKService: Initialized successfully');
    } catch (e) {
      _lastError = 'Failed to initialize SDK: $e';
      debugPrint('❌ XAppSDKService: $_lastError');
    }
  }

  /// Get token from x-app-sdk
  Future<String?> getToken() async {
    if (!await _ensureSdkReady()) {
      return null;
    }
    debugPrint('🔍 XAppSDKService: Getting token...');
    try {
      final result = await _invokeSdkMethod('getToken', ensureInitialized: false);
      if (result != null) {
        final status = getProperty(result, 'status')?.toString();
        final data = getProperty(result, 'data');
        final message = getProperty(result, 'message');
        final errorMessage = message?.toString();
        final tokenString = data?.toString().orEmpty ?? '';
        final isPlaceholder = tokenString.trim().toLowerCase().contains("tokenex.tokenex");
        print('🔍 XAppSDKService: getToken result - status: $status, token: ${tokenString}, isPlaceholder: $isPlaceholder');
        if (status == 'success' && data != null && tokenString.isNotEmpty && !isPlaceholder) {
          _cachedToken = tokenString;
          _lastError = null;
          final preview = tokenString.length > 8 ? tokenString.substring(0, 8) : tokenString;
          debugPrint('✅ XAppSDKService: Token retrieved successfully: $preview...');
          return _cachedToken;
        } else {
          final details = errorMessage?.isNotEmpty == true ? ' - $errorMessage' : '';
          _lastError = 'SDK returned status: $status$details';
          debugPrint('❌ XAppSDKService: $_lastError');
        }
      } else {
        _lastError ??= 'getToken returned null';
        debugPrint('❌ XAppSDKService: $_lastError');
      }
    } catch (e) {
      _lastError = 'Error getting token: $e';
      debugPrint('❌ XAppSDKService: $_lastError');
    }
    return null;
  }

  /// Close app and return to Super App
  Future<bool> closeApp([Map<String, dynamic>? data]) async {
    if (!await _ensureSdkReady()) {
      debugPrint('❌ XAppSDKService: closeApp skipped - SDK not ready');
      _fallbackClose();
      return false;
    }

    if (_browserMode) {
      debugPrint('ℹ️ XAppSDKService: Running in browser mode, falling back from closeApp');
      _fallbackClose();
      return false;
    }

    debugPrint('🔍 XAppSDKService: Closing app...');
    final result = await _invokeSdkMethod(
      'closeApp',
      args: data != null ? <dynamic>[data] : const <dynamic>[],
      expectPromise: false,
      ensureInitialized: false,
    );

    final success = _lastError == null && _isSuccessResult(result);
    if (success) {
      if (data != null) {
        debugPrint('✅ XAppSDKService: App closed with data: $data');
      } else {
        debugPrint('✅ XAppSDKService: App closed successfully');
      }
      return true;
    }

    final reason = _lastError ?? 'closeApp returned non-success result';
    debugPrint('❌ XAppSDKService: $reason');
    _fallbackClose();
    return false;
  }

  /// Config UI inside Super App
  Future<dynamic> configUIApp(Map<String, dynamic> config) =>
      _invokeAfterEnsure('configUIApp', args: <dynamic>[config]);

  /// Trigger a phone call via Super App
  Future<dynamic> callPhone(String phoneNumber) =>
      _invokeAfterEnsure('call', args: <dynamic>[phoneNumber]);

  /// Trigger sending SMS via Super App
  Future<dynamic> sendSms(String phoneNumber) =>
      _invokeAfterEnsure('sms', args: <dynamic>[phoneNumber]);

  /// Vibrate the device
  Future<dynamic> vibrate() => _invokeAfterEnsure('vibrate');

  /// Get current device location
  Future<dynamic> currentLocation() => _invokeAfterEnsure('currentLocation');

  /// Request location permission
  Future<dynamic> requestLocationPermission() =>
      _invokeAfterEnsure('requestLocationPermission');

  /// Open image picker
  Future<dynamic> openPickerImage(dynamic type) =>
      _invokeAfterEnsure('openPickerImage', args: <dynamic>[type]);

  /// Open file picker
  Future<dynamic> openPickerFile([dynamic options]) => options == null
      ? _invokeAfterEnsure('openPickerFile')
      : _invokeAfterEnsure('openPickerFile', args: <dynamic>[options]);

  /// Send payment request
  Future<dynamic> paymentRequest(Map<String, dynamic> payload) =>
      _invokeAfterEnsure('paymentRequest', args: <dynamic>[payload]);

  /// Listen to notification event
  Future<VoidCallback?> listenNotificationEvent(
    ValueChanged<dynamic> onEvent,
  ) async {
    final disposer = await _invokeAfterEnsure(
      'listenNotifiactionEvent',
      args: <dynamic>[
        js_interop.allowInterop((dynamic event) {
          try {
            onEvent(event);
          } catch (error, stackTrace) {
            debugPrint(
                '❌ XAppSDKService: Error in notification listener: $error');
            debugPrintStack(stackTrace: stackTrace);
          }
        }),
      ],
      expectPromise: false,
    );
    return _registerListenerDisposer('listenNotifiactionEvent', disposer);
  }

  /// Listen to payment events
  Future<VoidCallback?> listenPaymentEvent(
    ValueChanged<dynamic> onEvent,
  ) async {
    final disposer = await _invokeAfterEnsure(
      'listenPaymentEvent',
      args: <dynamic>[
        js_interop.allowInterop((dynamic event) {
          try {
            onEvent(event);
          } catch (error, stackTrace) {
            debugPrint('❌ XAppSDKService: Error in payment listener: $error');
            debugPrintStack(stackTrace: stackTrace);
          }
        }),
      ],
      expectPromise: false,
    );
    return _registerListenerDisposer('listenPaymentEvent', disposer);
  }

  /// Request permissions (SDK uses misspelled 'premissionsRequest')
  Future<dynamic> permissionsRequest(dynamic type) =>
      _invokeAfterEnsure('premissionsRequest', args: <dynamic>[type]);

  /// Alias for the SDK method name
  Future<dynamic> premissionsRequest(dynamic type) =>
      permissionsRequest(type);

  /// Persist data into Super App store
  Future<dynamic> saveStore(dynamic data) =>
      _invokeAfterEnsure('saveStore', args: <dynamic>[data]);

  /// Retrieve data from Super App store
  Future<dynamic> getStore() => _invokeAfterEnsure('getStore');

  /// Clear Super App store data
  Future<dynamic> clearStore() => _invokeAfterEnsure('clearStore');

  /// Get user info via SDK (returns the raw response map)
  Future<Map<String, dynamic>?> getInfo(dynamic key) async {
    if (!await _ensureSdkReady()) {
      return null;
    }

    debugPrint('🔍 XAppSDKService: Getting info for key: $key');
    try {
      final resolvedKey = await _resolveInfoKeyValue(key);
      final result = await _invokeSdkMethod(
        'getInfo',
        args: <dynamic>[resolvedKey ?? key],
        ensureInitialized: false,
      );
      if (result == null) {
        _lastError ??= 'getInfo returned null';
        debugPrint('❌ XAppSDKService: $_lastError');
        return null;
      }

      var infoMap = _convertDynamicToMap(result);
      if (infoMap == null || infoMap.isEmpty) {
        _lastError = 'getInfo returned invalid data';
        debugPrint('❌ XAppSDKService: $_lastError');
        return null;
      }

      if (_browserMode) {
        infoMap = _sanitizeBrowserInfoData(infoMap);
        debugPrint('ℹ️ XAppSDKService: Browser mode detected - returning empty info data');
      }

      _lastError = null;
      debugPrint('✅ XAppSDKService: Info retrieved for $key');
      return infoMap;
    } catch (e) {
      _lastError = 'Error getting info: $e';
      debugPrint('❌ XAppSDKService: $_lastError');
    }
    return null;
  }

  /// Clear cached token
  void clearToken() {
    _cachedToken = null;
    _lastError = null;
    debugPrint('🧹 XAppSDKService: Token cache cleared');
  }

  /// Reset service state
  void reset() {
    _disposeAllListeners();
    _isInitialized = false;
    _sdkModule = null;
    _cachedToken = null;
    _lastError = null;
    _browserMode = false;
    try {
      final loader = getProperty(html.window, '__xAppSdkLoader');
      if (loader != null && _hasProperty(loader, 'resetCachedModule')) {
        callMethod(loader, 'resetCachedModule', <dynamic>[]);
      }
    } catch (_) {}
    debugPrint('🔄 XAppSDKService: Service reset');
  }

  Future<bool> _ensureSdkReady() async {
    if (!kIsWeb) {
      debugPrint('⚠️ XAppSDKService: SDK requested on non-web platform');
      return false;
    }
    if (_isInitialized && _sdkModule != null) {
      return true;
    }
    debugPrint(
        '⚠️ XAppSDKService: SDK not initialized, attempting to initialize...');
    await initialize();
    return _isInitialized && _sdkModule != null;
  }

  Future<dynamic> _invokeAfterEnsure(
    String methodName, {
    List<dynamic> args = const <dynamic>[],
    bool expectPromise = true,
  }) async {
    if (!await _ensureSdkReady()) {
      return null;
    }
    return _invokeSdkMethod(
      methodName,
      args: args,
      expectPromise: expectPromise,
      ensureInitialized: false,
    );
  }

  Future<dynamic> _invokeSdkMethod(
    String methodName, {
    List<dynamic> args = const <dynamic>[],
    bool expectPromise = true,
    bool ensureInitialized = true,
  }) async {
    if (ensureInitialized && !await _ensureSdkReady()) {
      return null;
    }

    final sdk = await _loadSdkModule();
    if (sdk == null) {
      _lastError = 'x-app-sdk not available';
      debugPrint('❌ XAppSDKService: $_lastError');
      return null;
    }

    if (!_hasProperty(sdk, methodName)) {
      _lastError = '$methodName method not found in SDK';
      debugPrint('❌ XAppSDKService: $_lastError');
      return null;
    }

    try {
      final preparedArgs = args.isEmpty
          ? <dynamic>[]
          : List<dynamic>.from(args.map(_prepareArgument));
      final result = callMethod(sdk, methodName, preparedArgs);

      if (expectPromise) {
        try {
          final awaited = await promiseToFuture(result);
          _lastError = null;
          return awaited;
        } catch (_) {
          // Not a promise, fall through and return raw value.
        }
      }

      _lastError = null;
      return result;
    } catch (e) {
      _lastError = 'Error invoking $methodName: $e';
      debugPrint('❌ XAppSDKService: $_lastError');
      return null;
    }
  }

  dynamic _prepareArgument(dynamic value) {
    if (value == null) {
      return null;
    }
    if (value is Map || value is Iterable) {
      try {
        return jsify(value);
      } catch (_) {
        // Fall through and return value as-is if jsify fails.
      }
    }
    return value;
  }

  VoidCallback? _registerListenerDisposer(
    String methodName,
    dynamic disposer,
  ) {
    if (disposer == null) {
      if (_lastError != null) {
        debugPrint(
            '❌ XAppSDKService: Failed to register $methodName listener - $_lastError');
      } else {
        debugPrint(
            '⚠️ XAppSDKService: $methodName did not return a disposer function');
      }
      return null;
    }

    _listenerDisposers.add(disposer);
    debugPrint('🔔 XAppSDKService: $methodName listener registered');

    return () {
      _invokeJsFunction(disposer);
      _listenerDisposers.remove(disposer);
    };
  }

  void _invokeJsFunction(dynamic fn) {
    if (fn == null) {
      return;
    }
    try {
      final result = callMethod(fn, 'call', <dynamic>[null]);
      try {
        promiseToFuture(result);
      } catch (_) {
        // Ignore non-Promise results.
      }
    } catch (e) {
      debugPrint('❌ XAppSDKService: Failed to invoke JS callback: $e');
    }
  }

  void _disposeAllListeners() {
    if (_listenerDisposers.isEmpty) {
      return;
    }
    final disposers = List<dynamic>.from(_listenerDisposers);
    for (final disposer in disposers) {
      _invokeJsFunction(disposer);
    }
    _listenerDisposers.clear();
  }

  void _fallbackClose() {
    try {
      if (html.window.history.length > 1) {
        html.window.history.back();
      } else {
        html.window.close();
      }
      debugPrint('✅ XAppSDKService: Fallback close executed');
    } catch (fallbackError) {
      debugPrint('❌ XAppSDKService: Fallback close failed: $fallbackError');
    }
  }

  bool _isSuccessResult(dynamic result) {
    if (result == null) {
      return true;
    }
    if (result is bool) {
      return result;
    }
    if (result is Map) {
      final status = result['status']?.toString().toLowerCase();
      if (status != null) {
        return status == 'success';
      }
      if (result.containsKey('success')) {
        final successValue = result['success'];
        if (successValue is bool) {
          return successValue;
        }
        return successValue?.toString().toLowerCase() == 'true';
      }
    }
    try {
      if (_hasProperty(result, 'status')) {
        final status = getProperty(result, 'status');
        if (status is String) {
          return status.toLowerCase() == 'success';
        }
      }
      if (_hasProperty(result, 'success')) {
        final successValue = getProperty(result, 'success');
        if (successValue is bool) {
          return successValue;
        }
        return successValue?.toString().toLowerCase() == 'true';
      }
    } catch (_) {}
    return true;
  }

  Future<dynamic> _loadSdkModule() async {
    if (_sdkModule != null) {
      _browserMode = _detectBrowserMode(_sdkModule);
      return _sdkModule;
    }

    // Access loader API exposed in JS
    final loader = getProperty(html.window, '__xAppSdkLoader');
    if (loader == null) {
      _lastError = 'x-app-sdk loader not found on global scope';
      debugPrint('❌ XAppSDKService: $_lastError');
      return null;
    }

    final hasLoadFunction = _hasProperty(loader, 'loadXAppSdkModule');
    if (!hasLoadFunction) {
      _lastError = 'x-app-sdk loader missing loadXAppSdkModule';
      debugPrint('❌ XAppSDKService: $_lastError');
      return null;
    }

    try {
      final module = await promiseToFuture(
        callMethod(loader, 'loadXAppSdkModule', <dynamic>[]),
      );
      if (module == null) {
        _lastError = 'x-app-sdk module resolved to null';
        debugPrint('❌ XAppSDKService: $_lastError');
        return null;
      }

      final source = getProperty(module, '__xAppSdkSource');
      if (source != null) {
        debugPrint('🔗 XAppSDKService: Module loaded from $source');
      }

      if (!_hasProperty(module, 'getToken') ||
          !_hasProperty(module, 'closeApp')) {
        _lastError = 'x-app-sdk module missing required exports';
        debugPrint('❌ XAppSDKService: $_lastError');
        return null;
      }

      _sdkModule = module;
      _browserMode = _detectBrowserMode(module);
      return _sdkModule;
    } catch (e) {
      _lastError = 'Failed to load x-app-sdk module: $e';
      debugPrint('❌ XAppSDKService: $_lastError');
      return null;
    }
  }

  bool _hasProperty(dynamic target, String name) {
    try {
      final value = getProperty(target, name);
      return value != null;
    } catch (_) {
      return false;
    }
  }

  bool _detectBrowserMode(dynamic module) {
    try {
      final fltSdk = getProperty(module, 'fltSDK');
      if (fltSdk != null && _hasProperty(fltSdk, 'getBrowserMode')) {
        final mode = callMethod(fltSdk, 'getBrowserMode', <dynamic>[]);
        if (mode is bool) {
          return mode;
        }
      }
    } catch (_) {}
    return false;
  }

  Future<dynamic> _resolveInfoKeyValue(dynamic key) async {
    if (key == null) {
      return null;
    }
    if (key is! String) {
      return key;
    }
    final normalized = key.trim();
    if (normalized.isEmpty) {
      return normalized;
    }

    final candidates = <String>[];
    final lower = normalized.toLowerCase();
    candidates.add(normalized);
    final upper = normalized.toUpperCase();
    if (upper != normalized) {
      candidates.add(upper);
    }
    if (lower != normalized && lower != upper) {
      candidates.add(lower);
    }
    final aliasProperty = _infoKeyPropertyMap[lower];
    if (aliasProperty != null && !candidates.contains(aliasProperty)) {
      candidates.add(aliasProperty);
    }

    final module = await _loadSdkModule();
    if (module != null) {
      try {
        final infoEnum = getProperty(module, 'EKeyInfor');
        if (infoEnum != null) {
          for (final candidate in candidates) {
            try {
              final value = getProperty(infoEnum, candidate);
              if (value != null) {
                return value;
              }
            } catch (_) {}
          }
        }
      } catch (_) {}
    }

    return normalized;
  }

  Map<String, dynamic>? _convertDynamicToMap(dynamic source) {
    if (source == null) {
      return null;
    }
    if (source is Map<String, dynamic>) {
      return source.map((key, value) => MapEntry(key, _normalizeDynamicValue(value)));
    }
    if (source is Map) {
      final normalized = <String, dynamic>{};
      source.forEach((key, dynamic value) {
        if (key == null) {
          return;
        }
        normalized[key.toString()] = _normalizeDynamicValue(value);
      });
      return normalized;
    }
    try {
      final dartValue = js_interop.dartify(source);
      if (dartValue is Map) {
        return _convertDynamicToMap(dartValue);
      }
    } catch (_) {}
    return null;
  }

  dynamic _normalizeDynamicValue(dynamic value) {
    if (value is Map) {
      return _convertDynamicToMap(value);
    }
    if (value is Iterable) {
      return List<dynamic>.from(value.map(_normalizeDynamicValue));
    }
    return value;
  }

  Map<String, dynamic> _sanitizeBrowserInfoData(Map<String, dynamic> payload) {
    final sanitized = Map<String, dynamic>.from(payload);
    if (sanitized.containsKey('data')) {
      sanitized['data'] = <String, dynamic>{};
      return sanitized;
    }
    return <String, dynamic>{};
  }
}
