import 'dart:convert';

import 'package:commons_core/commons_core.dart';
import 'package:flutter/services.dart' show rootBundle;

import '../security/secret_store.dart';

/// The web-of-trust bootstrap referents (Duniter "seeds"): identities every
/// membership calculation trusts by construction. The network's roots.
///
/// Sources, unioned: a bundled asset (`assets/trust/referents.json`, a curated
/// founding set — empty until real founders are added) plus referents the user
/// adds in-app (by npub / QR), kept in the keystore. All keys are stored and
/// returned as 64-char hex. Solving cold-start honestly: no invented identities
/// ship, and a user can bootstrap their own roots.
class TrustReferents {
  TrustReferents(this._store, {this.assetPath = 'assets/trust/referents.json'});

  final SecretStore _store;
  final String assetPath;

  static const _userKey = 'tane.social.trust.referents';

  List<String>? _bundledCache;

  /// Every referent (bundled ∪ user-added), as hex public keys.
  Future<Set<String>> all() async =>
      {...await _bundled(), ...await userAdded()};

  /// Referents the user added themselves (hex), for the management UI.
  Future<Set<String>> userAdded() async {
    final raw = await _store.read(_userKey);
    if (raw == null || raw.isEmpty) return {};
    return raw.split('\n').where((s) => s.isNotEmpty).toSet();
  }

  /// Adds a referent from an [npubOrHex]; returns its hex form. Idempotent.
  /// Throws [FormatException] if it is neither an npub nor a hex key.
  Future<String> add(String npubOrHex) async {
    final hex = npubToHex(npubOrHex); // validates
    final current = await userAdded();
    if (current.add(hex)) {
      await _store.write(_userKey, current.join('\n'));
    }
    return hex;
  }

  /// Removes a user-added referent (bundled ones can't be removed).
  Future<void> remove(String hex) async {
    final current = await userAdded()..remove(hex);
    await _store.write(_userKey, current.join('\n'));
  }

  Future<List<String>> _bundled() async {
    final cached = _bundledCache;
    if (cached != null) return cached;
    try {
      final decoded = jsonDecode(await rootBundle.loadString(assetPath));
      final list = (decoded is Map ? decoded['referents'] : decoded) as List?;
      final out = <String>[];
      for (final entry in list ?? const []) {
        try {
          out.add(npubToHex(entry as String));
        } catch (_) {
          // Skip a malformed entry rather than break the whole set.
        }
      }
      return _bundledCache = out;
    } catch (_) {
      return _bundledCache = const []; // no/invalid asset → no bundled referents
    }
  }
}
