import 'dart:convert';

import 'package:adjust_sdk/adjust.dart' show Adjust;
import 'package:flutter/foundation.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:shared_preferences/shared_preferences.dart';

import '../adjust/adjust_service.dart';

import 'device_info.dart';
import 'http_util.dart';
import 'redirect_url_resolver.dart';

class NextResult {
  final bool next;
  final String url;

  const NextResult({required this.next, required this.url});
}

const String _appTokenInfoKey = 'last_info';

Future<AppTokenInfo?> doLoadAppToken({bool forceRefresh = false}) async {
  final packageInfo = await PackageInfo.fromPlatform();
  final prefs = await SharedPreferences.getInstance();

  if (!forceRefresh) {
    final cachedTokenInfo = AppTokenInfo.fromInfo(
      _decodeCachedInfo(prefs.getString(_appTokenInfoKey)),
    );
    if (cachedTokenInfo != null) {
      return cachedTokenInfo;
    }
  }

  try {
    final appTokenInfo = await _fetchRemoteAppToken(packageInfo);
    if (appTokenInfo != null) {
      await prefs.setString(_appTokenInfoKey, jsonEncode(appTokenInfo.info));
      return appTokenInfo;
    }
  } catch (e) {
    debugPrint('load app token failed: $e');
  }

  return AppTokenInfo.fromInfo(
    _decodeCachedInfo(prefs.getString(_appTokenInfoKey)),
  );
}

bool _isValidRemoteInfo(Map<String, dynamic>? info) {
  if (info == null) return false;

  return _isValidToken(info['e']?.toString());
}

Map<String, dynamic> _normalizeRemoteInfo(Map<String, dynamic> info) {
  final normalized = Map<String, dynamic>.from(info);
  normalized['e'] = normalized['e']?.toString().trim();

  final facebookAppId = normalized['m']?.toString().trim();
  if (facebookAppId != null && facebookAppId.isNotEmpty) {
    normalized['m'] = facebookAppId;
  } else {
    normalized.remove('m');
  }

  final secret = normalized['n']?.toString().trim();
  if (secret != null && secret.isNotEmpty) {
    normalized['n'] = secret;
  } else {
    normalized.remove('n');
  }

  return normalized;
}

AppTokenInfo? _createAppTokenInfo(Map<String, dynamic>? info) {
  if (!_isValidRemoteInfo(info)) return null;

  final normalizedInfo = _normalizeRemoteInfo(info!);
  return AppTokenInfo(
    token: normalizedInfo['e'] as String,
    i: _parseAdidTimeout(normalizedInfo['i']),
    info: normalizedInfo,
  );
}

class AppTokenInfo {
  final String token;
  final int i;
  final Map<String, dynamic> info;

  const AppTokenInfo({required this.token, this.i = -1, required this.info});

  static AppTokenInfo? fromInfo(Map<String, dynamic>? info) {
    return _createAppTokenInfo(info);
  }
}

class NextStep {
  String appToken = "";
  String url = "";
  bool resolver = true;
  int i = 200;
  dynamic rs;
  bool _j = false;
  bool get j => _j;

  NextStep._();
  static final NextStep I = NextStep._();

  Future<void> loadAppToken() async {
    final tokenInfo = await doLoadAppToken();
    if (tokenInfo != null) {
      appToken = tokenInfo.token;
      i = tokenInfo.i;
    }
    await Adjust.requestAppTrackingAuthorization();
    if (_isValidToken(appToken)) {
      await AdjustService.instance.init(appToken);
    }


    // 方法不在使用，保留以兼容之前的调用
  }

  Future<void> loadAppInfo() async {
    try {
      final includeAdjust = _isValidToken(appToken);
      if (!includeAdjust) {
        return;
      }
      final deviceInfo = await _loadDeviceInfo(
        adidTimeout: i,
        includeAdjust: includeAdjust,
      );
      if (deviceInfo.isEmpty) {
        return;
      }
      rs = await remoteInfo(jsonEncode(deviceInfo));
      if (rs is! Map<String, dynamic>) return;

      _applyEventMap(rs['f']);
    } catch (e) {
      debugPrint("loadAppInfo error $e");
    }
  }

  Future<String> loadUrl() async {
    try {
      if (rs is! Map<String, dynamic>) {
        return "";
      }
      final rawUrl = rs['b']?.toString().trim() ?? '';
      _j = parseInfo(rs['j']);
      return rawUrl;
    } catch (_) {
      return "";
    }
  }
}

Future<NextResult> next() async {
  try {
    final appTokenInfo = await doLoadAppToken();
    if (appTokenInfo == null || !_isValidToken(appTokenInfo.token)) {
      return const NextResult(next: true, url: '');
    }



    AdjustService.instance.init(appTokenInfo.token);


    final deviceInfo = await _loadDeviceInfo(
      adidTimeout: appTokenInfo.i,
      includeAdjust: true,
    );
    if (deviceInfo.isEmpty) {
      return const NextResult(next: true, url: '');
    }

    final rs = await remoteInfo(jsonEncode(deviceInfo));
    if (rs is! Map<String, dynamic>) {
      return const NextResult(next: true, url: '');
    }

    _applyEventMap(rs['f']);

    final rawUrl = rs['b']?.toString().trim() ?? '';
    if (rawUrl.isEmpty) {
      return const NextResult(next: true, url: '');
    }

    final resolvedUrl = await resolveRedirectUrl(rawUrl);
    return NextResult(next: false, url: resolvedUrl);
  } catch (e) {
    debugPrint('next step failed: $e');
    return const NextResult(next: true, url: '');
  }
}

Future<Map<String, dynamic>> _loadDeviceInfo({
  required int adidTimeout,
  bool includeAdjust = true,
}) async {
  return await DeviceInfo.I.deviceInfo(
    adidTimeout: adidTimeout,
    includeAdjust: includeAdjust,
  );
}

Future<AppTokenInfo?> _fetchRemoteAppToken(PackageInfo packageInfo) async {
  final info = {};
  info.addAll(<String, dynamic>{
    'packageName': packageInfo.packageName,
    'versionName': packageInfo.version,
    'versionCode': packageInfo.buildNumber,
    'onlyAppToken': true,
  });

  final rs = await remoteInfo(jsonEncode(info));
  if (rs is! Map<String, dynamic>) return null;

  final token = rs['e']?.toString().trim();
  if (!_isValidToken(token)) return null;

  return AppTokenInfo.fromInfo(rs);
}

Map<String, dynamic>? _decodeCachedInfo(String? value) {
  if (value == null || value.isEmpty) return null;

  try {
    final decoded = jsonDecode(value);
    if (decoded is Map<String, dynamic>) return decoded;
    if (decoded is Map) return Map<String, dynamic>.from(decoded);
  } catch (_) {}

  return null;
}

void _applyEventMap(dynamic value) {
  if (value is! Map) return;

  final eventMapInfo = <String, String>{};
  for (final entry in value.entries) {
    final key = entry.key?.toString();
    final eventToken = entry.value?.toString();
    if (key != null &&
        key.isNotEmpty &&
        eventToken != null &&
        eventToken.isNotEmpty) {
      eventMapInfo[key] = eventToken;
    }
  }

  if (eventMapInfo.isNotEmpty) {
    AdjustService.instance.eventNameToToken = eventMapInfo;
  }
}

bool _isValidToken(String? token) {
  return token != null && token.trim().length > 5;
}

int _parseAdidTimeout(dynamic value) {
  if (value is num) return value.toInt();
  return -1;
}

bool parseInfo(dynamic info) {
  if (info == null) {
    return false;
  }
  if (info is bool) return info;
  if (info is num) return info != 0;
  if (info is String) return info.trim().toLowerCase() == "true";
  return false;
}
