import 'dart:async';
import 'dart:math';
import 'package:uuid/uuid.dart';
import 'package:flutter/material.dart';
import 'models/telemetry_event.dart';
import 'models/device_context.dart';
import 'models/telemetry_config.dart';
import 'collectors/device_info_collector.dart';
import 'storage/telemetry_storage.dart';
import 'uploader/telemetry_uploader.dart';
import 'session/session_manager.dart';
import 'presence/heartbeat_service.dart';
import 'presence/presence_config.dart';

/// 遥测服务 - TelemetryService
///
/// 功能概述:
/// 收集应用使用数据，用于统计 DAU（日活跃用户）和在线用户数。
/// 支持事件上报、会话追踪、心跳检测等功能。
///
/// 主要功能:
/// 1. 事件记录 - 记录用户行为、页面访问、API调用、错误等
/// 2. 会话管理 - 追踪应用前后台切换，计算会话时长
/// 3. 心跳服务 - 定期上报在线状态，用于统计在线用户
/// 4. 设备信息 - 收集设备型号、系统版本、应用版本等
///
/// 使用方式:
/// ```dart
/// // 初始化
/// await TelemetryService().initialize(
///   apiBaseUrl: 'https://api.example.com',
///   context: context,
/// );
///
/// // 记录事件
/// TelemetryService().logPageView('home');
/// TelemetryService().logUserAction('button_click');
/// ```
///
/// 隐私说明:
/// - 支持用户选择是否开启遥测
/// - 支持远程配置开关
/// - 不收集用户敏感信息
class TelemetryService {
  static TelemetryService? _instance;
  TelemetryService._();

  factory TelemetryService() {
    _instance ??= TelemetryService._();
    return _instance!;
  }

  final _storage = TelemetryStorage();
  late TelemetryUploader _uploader;

  DeviceContext? _deviceContext;

  late String _installId;
  String get installId => _installId;

  String? _userId;
  String? get userId => _userId;

  String? _accessToken;

  bool _isInitialized = false;
  bool get isInitialized => _isInitialized;

  late SessionManager _sessionManager;
  late HeartbeatService _heartbeatService;

  Timer? _configSyncTimer;

  Future<void> initialize({
    required String apiBaseUrl,
    required BuildContext context,
    String? userId,
    Duration configSyncInterval = const Duration(hours: 1),
    PresenceConfig? presenceConfig,
  }) async {
    if (_isInitialized) return;

    await _storage.init();
    await _initInstallId();
    await TelemetryConfig().loadUserOptIn();

    TelemetryConfig().syncFromRemote(apiBaseUrl).catchError((e) {
      debugPrint('[Telemetry] Remote config sync failed (non-blocking): $e');
    });

    _deviceContext = await DeviceInfoCollector().collect(context);
    await _storage.saveDeviceContext(_deviceContext!.toJson());

    _userId = userId;

    _uploader = TelemetryUploader(
      apiBaseUrl: apiBaseUrl,
      storage: _storage,
      getAuthHeaders: _getAuthHeaders,
    );

    if (TelemetryConfig().globalEnabled) {
      _uploader.startPeriodicUpload();
    }

    _configSyncTimer = Timer.periodic(configSyncInterval, (_) async {
      await TelemetryConfig().syncFromRemote(apiBaseUrl);

      if (TelemetryConfig().globalEnabled) {
        _uploader.startPeriodicUpload();
      } else {
        _uploader.stopPeriodicUpload();
      }

      if (TelemetryConfig().presenceConfig != null) {
        _heartbeatService.updateConfig(TelemetryConfig().presenceConfig!);
      }
    });

    _sessionManager = SessionManager();
    _sessionManager.initialize(this);

    _heartbeatService = HeartbeatService();
    _heartbeatService.initialize(
      apiBaseUrl: apiBaseUrl,
      config: presenceConfig ?? TelemetryConfig().presenceConfig,
      getInstallId: () => _installId,
      getUserId: () => _userId,
      getAppVersion: () => _deviceContext?.appVersion ?? 'unknown',
      getAuthHeaders: _getAuthHeaders,
    );

    _isInitialized = true;
    debugPrint('TelemetryService initialized');
    debugPrint('   InstallId: $_installId');
    debugPrint('   UserId: $_userId');
  }

  Future<void> _initInstallId() async {
    final storedId = _storage.getInstallId();

    if (storedId != null) {
      _installId = storedId;
    } else {
      _installId = const Uuid().v4();
      await _storage.saveInstallId(_installId);
    }

    debugPrint('[Telemetry] Install ID: $_installId');
  }

  Map<String, String> _getAuthHeaders() {
    if (_accessToken == null) return {};
    return {'Authorization': 'Bearer $_accessToken'};
  }

  void setAccessToken(String? token) {
    _accessToken = token;
  }

  void logEvent(
    String eventName, {
    EventType type = EventType.userAction,
    EventLevel level = EventLevel.info,
    Map<String, dynamic>? properties,
  }) {
    if (!_isInitialized) {
      debugPrint('TelemetryService not initialized, event ignored');
      return;
    }

    if (!TelemetryConfig().shouldLog(type, eventName)) {
      return;
    }

    if (_needsSampling(type)) {
      if (Random().nextDouble() > TelemetryConfig().samplingRate) {
        return;
      }
    }

    final deviceProps = _deviceContext != null
        ? {
            'device_brand': _deviceContext!.brand,
            'device_model': _deviceContext!.model,
            'device_os': _deviceContext!.osVersion,
            'app_version': _deviceContext!.appVersion,
            'locale': _deviceContext!.locale,
          }
        : <String, dynamic>{};

    final event = TelemetryEvent(
      eventId: const Uuid().v4(),
      type: type,
      level: level,
      name: eventName,
      properties: {...deviceProps, ...?properties},
      timestamp: DateTime.now(),
      userId: _userId,
      sessionId: _sessionManager.currentSessionId,
      installId: _installId,
      deviceContextId: _deviceContext!.androidId,
    );

    _storage.enqueueEvent(event);
    _uploader.uploadIfNeeded();
  }

  bool _needsSampling(EventType type) {
    return type != EventType.error &&
        type != EventType.crash &&
        type != EventType.session;
  }

  void logPageView(String pageName, {Map<String, dynamic>? extra}) {
    logEvent(
      'page_view',
      type: EventType.pageView,
      properties: {'page': pageName, ...?extra},
    );
  }

  void logUserAction(String action, {Map<String, dynamic>? properties}) {
    logEvent(
      action,
      type: EventType.userAction,
      properties: properties,
    );
  }

  void logError(
    String errorMessage, {
    Object? error,
    StackTrace? stackTrace,
    Map<String, dynamic>? extra,
  }) {
    logEvent(
      'error_occurred',
      type: EventType.error,
      level: EventLevel.error,
      properties: {
        'message': errorMessage,
        'error': error?.toString(),
        'stack_trace': stackTrace?.toString(),
        ...?extra,
      },
    );
  }

  void logApiCall({
    required String url,
    required String method,
    required int statusCode,
    required int durationMs,
    String? error,
  }) {
    logEvent(
      'api_call',
      type: EventType.apiCall,
      level: error != null ? EventLevel.error : EventLevel.info,
      properties: {
        'url': url,
        'method': method,
        'status_code': statusCode,
        'duration_ms': durationMs,
        'error': error,
      },
    );
  }

  void logPerformance(
    String metricName, {
    required int durationMs,
    Map<String, dynamic>? extra,
  }) {
    logEvent(
      metricName,
      type: EventType.performance,
      properties: {'duration_ms': durationMs, ...?extra},
    );
  }

  void setUserId(String? userId) {
    _userId = userId;
    debugPrint('[Telemetry] User ID set: $userId');
  }

  void clearUserId() {
    _userId = null;
    debugPrint('[Telemetry] User ID cleared');
  }

  Future<void> flushOnBackground() async {
    await _uploader.uploadBatch(batchSize: 50);
    debugPrint('[Telemetry] Flushed on background');
  }

  Future<void> pauseForLogout() async {
    _uploader.stopPeriodicUpload();
    await _storage.clearEventQueue();
    _userId = null;
    debugPrint('[Telemetry] Paused for logout');
  }

  void resumeAfterLogin() {
    if (TelemetryConfig().globalEnabled) {
      _uploader.startPeriodicUpload();
      debugPrint('[Telemetry] Resumed after login');
    }
  }

  Future<void> setUserOptIn(bool optIn) async {
    await TelemetryConfig().setUserOptIn(optIn);

    if (!optIn) {
      _uploader.stopPeriodicUpload();
      await _storage.clearEventQueue();
      debugPrint('Telemetry disabled by user');
    } else {
      if (TelemetryConfig().globalEnabled) {
        _uploader.startPeriodicUpload();
      }
      debugPrint('Telemetry enabled by user');
    }
  }

  String? get currentSessionId => _sessionManager.currentSessionId;
  int get sessionDurationSeconds => _sessionManager.sessionDurationSeconds;
  bool get isHeartbeatRunning => _heartbeatService.isRunning;
  int get heartbeatCount => _heartbeatService.heartbeatCount;

  void updatePresenceConfig(PresenceConfig config) {
    _heartbeatService.updateConfig(config);
  }

  DeviceContext? get deviceContext => _deviceContext;

  Future<void> dispose() async {
    _configSyncTimer?.cancel();
    _sessionManager.dispose();
    _heartbeatService.dispose();
    await _uploader.forceUploadAll();
    _isInitialized = false;
    debugPrint('TelemetryService disposed');
  }

  static void reset() {
    _instance = null;
  }
}
