import 'dart:convert';

import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';

import '../../data/models/site_settings_properties_model.dart';
import '../../data/repositories/site_settings_properties_repository.dart';
import '../config/app_config.dart';

class CurrencyService extends GetxService {
  CurrencyService(this._repo);

  final SiteSettingsPropertiesRepository _repo;
  final _box = GetStorage();

  final Rx<CurrencyModel?> _current = Rx<CurrencyModel?>(null);
  CurrencyModel? get current => _current.value;

  final RxList<CurrencyModel> currencies = <CurrencyModel>[].obs;

  List<CurrencyModel>? _memList;
  DateTime? _memTs;

  bool inflight = false;

  static bool verbose = false;

  static const Duration currencyTtl = Duration(days: 3);

  static const String _kCache = 'currencies_cache_json';
  static const String _kTs = 'currencies_cache_ts';

  Future<void> load({bool force = false}) async {
    if (!force && _memList != null && _memTs != null) {
      final fresh = DateTime.now().difference(_memTs!) < currencyTtl;
      if (fresh) {
        currencies.assignAll(_memList!);
        return;
      }
    }

    if (!force) {
      final cachedStr = _box.read<String>(_kCache);
      final tsStr = _box.read<String>(_kTs);
      final ts = tsStr != null ? DateTime.tryParse(tsStr) : null;

      if (cachedStr != null && ts != null) {
        final fresh = DateTime.now().difference(ts) < currencyTtl;
        if (fresh) {
          try {
            final list = (json.decode(cachedStr) as List)
                .whereType<Map<String, dynamic>>()
                .map(CurrencyModel.fromJson)
                .toList();
            _memList = list;
            _memTs = ts;
            currencies.assignAll(list);
            return;
          } catch (_) {}
        }
      }
    }

    inflight = true;
    try {
      final list = await _repo.fetchCurrencies();

      _memList = list;
      _memTs = DateTime.now();
      currencies.assignAll(list);

      final raw = list.map((e) => e.toJson()).toList();
      _box.write(_kCache, json.encode(raw));
      _box.write(_kTs, _memTs!.toIso8601String());
    } finally {
      inflight = false;
    }
  }

  void setCurrency(CurrencyModel c, {bool persist = true}) {
    _current.value = c;
    if (persist) {
      _box.write(AppConfig.kCurrencyCode, c.code);
    }
  }

  void restoreFrom(List<CurrencyModel> list) {
    final savedCode = _box.read<String>(AppConfig.kCurrencyCode);
    CurrencyModel? picked;

    if (savedCode != null && savedCode.isNotEmpty) {
      picked = list.firstWhereOrNull(
        (e) => e.code.toUpperCase() == savedCode.toUpperCase(),
      );
    }
    picked ??= list.firstWhereOrNull((e) => e.code.toUpperCase() == 'USD');
    picked ??= list.isNotEmpty ? list.first : null;

    if (picked != null) {
      setCurrency(picked, persist: false);
    }
  }

  String format(double amount, {bool applyConversion = false}) {
    final c = current;
    if (c == null) return amount.toStringAsFixed(2);

    final num = applyConversion ? amount * c.conversionRate : amount;
    final fixed = num.toStringAsFixed(c.numberOfDecimal);

    final parts = fixed.split('.');
    final intPart = parts[0];
    final decPart = parts.length > 1 ? parts[1] : '';

    final withThousands = _withThousands(intPart, c.thousandSeparator);
    final numberStr = decPart.isEmpty
        ? withThousands
        : '$withThousands${c.decimalSeparator}$decPart';

    return c.position == '1'
        ? '${c.symbol}$numberStr'
        : '$numberStr${c.symbol}';
  }

  String _withThousands(String digits, String sep) {
    final buf = StringBuffer();
    int count = 0;
    for (int i = digits.length - 1; i >= 0; i--) {
      buf.write(digits[i]);
      count++;
      if (count == 3 && i != 0) {
        buf.write(sep);
        count = 0;
      }
    }
    return buf.toString().split('').reversed.join();
  }

  void clearCache() {
    _memList = null;
    _memTs = null;
    _box.remove(_kCache);
    _box.remove(_kTs);
  }
}
