import 'dart:convert';
import 'dart:ui';

import 'package:fis_common/logger/logger.dart';
import 'package:fis_i18n/i18n.dart';
import 'package:fis_jsonrpc/services/index.dart';
import 'package:flyinsono/rpc.dart';
import 'package:flyinsono/architecture/storage/shared_storage.dart';
import 'package:get/get.dart';

///地区数据缓存
mixin _AreaRegionFinderMixin {
  var _cache = <AreaRegionModel>[];

  /// 全部数据
  List<AreaRegionModel> get allList => _cache;

  /// 获取顶级区域（国家）集合
  Iterable<AreaRegionModel> findTopGradeList() {
    final list = _cache.where((x) => x.grade == "0");
    return list;
  }

  /// 根据父级Code获取集合
  ///
  /// [parentCode] 父级Code
  Iterable<AreaRegionModel> findListByParentCode(String parentCode) {
    final children = _cache.where((x) => x.parentCode == parentCode);
    return children;
  }

  /// 根据Code获取当前集合
  ///
  /// [currCode] 当前Code
  Iterable<AreaRegionModel> findListByCode(String currCode) {
    final children = _cache.where((x) => x.code == currCode);

    return children;
  }

  /// 根据name获取集合
  ///
  /// [code] Code [parentCode] parentCode
  List<String> findListName(
    String parentCode,
    String code,
  ) {
    final list = _cache.where((x) => x.grade == "0");
    final father = _cache.where((x) => x.code == parentCode);
    final children = _cache.where((x) => x.code == code);
    return [
      list.toList().first.name,
      father.toList().first.name,
      children.toList().first.name
    ];
  }

  /// 判断是否`中国`的code
  bool judgeIsChinaCode(String code) => code == "1000000";
}

///地区缓存数据服务
class AreaRegionService extends GetxService with _AreaRegionFinderMixin {
  final storageService = Get.find<SharedStorageService>();
  static const _STORAGE_KEY = "REGION_TEMP";
  // var _cache = <AreaRegionModel>[];
  var _version = "";
  var _locale = "";

  String get _storageKey => "$_STORAGE_KEY-v$_version";

  Future<void> init() async {
    // TODO: from db
    _version = "0.8";
    _cacheLocale(i18nBook.locale);
    await _loadCacheData();
  }

  ///检查区域数据版本
  ///判断是否进行缓存的更新
  Future<bool> checkUpdate() async {
    try {
      final result = await rpc.region.getRegionsAsync(
        GetRegionsRequest(version: _version, languageType: _locale),
      );
      if (result.regionVersion != _version && result.reginData != null) {
        _version = result.regionVersion!; // TODO: save db
        _cache.clear();
        final dataMap = jsonDecode(result.reginData!);
        final List list = dataMap['datas']!;
        list.forEach((v) {
          final model = AreaRegionModel.fromJson(v);
          _cache.add(model);
        });
        // TODO: save db
        await _saveCacheData();
        return true;
      }
    } catch (e) {
      logger.e("AreaRegionService - call `checkUpdate` error.", e);
    }
    return false;
  }

  Future<void> _loadCacheData() async {
    final list = storageService.getList(_storageKey);
    if (list.isEmpty) {
      _cache = [];
      return;
    }
    list.forEach((v) {
      final map = jsonDecode(v);
      _cache.add(AreaRegionModel.fromJson(map));
    });
  }

  Future<void> _saveCacheData() async {
    final list = _cache.map((x) => jsonEncode(x.toJson())).toList();
    await storageService.setList(_storageKey, list);
  }

  void _cacheLocale(Locale locale) {
    final codeStringBuffer = StringBuffer(locale.languageCode);
    if (locale.countryCode != null && locale.countryCode!.isNotEmpty) {
      codeStringBuffer.write('-${locale.countryCode}');
    }
    _locale = codeStringBuffer.toString();
  }
}

///地区信息数据结构实例
class AreaRegionModel {
  AreaRegionModel({
    required this.grade,
    required this.parentCode,
    required this.code,
    required this.name,
  });
  final String code;
  final String grade;
  final String parentCode;
  final String name;

  ///地区信息工厂构造
  factory AreaRegionModel.fromJson(Map<String, dynamic> map) {
    return AreaRegionModel(
      grade: map['g'],
      parentCode: map['f'],
      code: map['c'],
      name: map['n'],
    );
  }

  ///地区信息序列化
  Map<String, dynamic> toJson() {
    final map = <String, dynamic>{};
    map['g'] = grade;
    map['f'] = parentCode;
    map['c'] = code;
    map['n'] = name;
    return map;
  }
}
