import 'dart:async' show Future;
import 'dart:convert' show json;
import 'package:pb_authenticator_state/file_storage_base.dart';
import 'package:pb_authenticator_totp/totp.dart';

import './legacy_authenticator_item.dart';
import '../repository/repository_base.dart' show RepositoryBase;

/// Is used to load/save state to JSON file.
///
/// Currently uses FileStorage to do so.
class LegacyRepository implements RepositoryBase<LegacyAuthenticatorItem> {
  LegacyRepository(this._fileStorage);

  // Current file version
  static const _currentVersion = 1;

  // Filename of JSON
  static const _stateFilename = "items.json";

  // Used for loading/saving files
  final FileStorageBase _fileStorage;

  // Items
  List<LegacyAuthenticatorItem> _items = [];

  Future<bool> hasState() {
    return _fileStorage.hasFile(_stateFilename);
  }

  /// Loads state from storage.
  @override
  Future<List<LegacyAuthenticatorItem>> loadItems() async {
    // If file doesn't exist, then initialise empty state

    // Load/decode file
    var str = await _fileStorage.readFile(_stateFilename);
    if (str == null) {
      return [];
    }

    Map<String, dynamic> decoded = json.decode(str);
    var version = decoded['version'];

    // If version is not current, bring it up to current
    if (version != _currentVersion) {
      throw Exception("Unknown file version");
    }

    // Decode and return items
    // Adapted from: https://stackoverflow.com/questions/50360443
    var itemsDecoded = decoded['items'];
    var items = (itemsDecoded as List)
        .map((i) => LegacyAuthenticatorItem.fromMap(i))
        .toList();
    _items = items;
    return items;
  }

  /// Saves [state] to storage.
  @override
  Future replaceItems(List<LegacyAuthenticatorItem> state) async {
    return await _saveItems(state);
  }

  @override
  Future<LegacyAuthenticatorItem> addItem(TotpItem item) async {
    var legacyAuthenticatorItem =
        LegacyAuthenticatorItem.newAuthenticatorItem(item);
    _items.add(legacyAuthenticatorItem);
    await _saveItems(_items);
    return legacyAuthenticatorItem;
  }

  Future _saveItems(List<LegacyAuthenticatorItem> items) async {
    // Encode
    var itemsMapped = items.map((i) => i.toMap()).toList();
    var version = _currentVersion;
    var str = json.encode({'items': itemsMapped, 'version': version});

    // Save to file
    return await _fileStorage.writeFile(_stateFilename, str).then((_) => {});
  }
}
