import 'package:result_monad/result_monad.dart';

import '../../models/connection.dart';
import '../../models/exec_error.dart';
import '../interfaces/connections_repo_intf.dart';

class MemoryConnectionsRepo implements IConnectionsRepo {
  final _connectionsById = <String, Connection>{};
  final _connectionsByName = <String, Connection>{};
  final _myContacts = <Connection>[];

  @override
  void clear() {
    _connectionsById.clear();
    _connectionsByName.clear();
    _myContacts.clear();
  }

  @override
  List<Connection> getKnownUsersByName(String name) {
    return _connectionsByName.values.where((it) {
      final normalizedHandle = it.handle.toLowerCase();
      final normalizedName = it.name.toLowerCase();
      final normalizedQuery = name.toLowerCase();
      return normalizedHandle.contains(normalizedQuery) ||
          normalizedName.contains(normalizedQuery);
    }).toList();
  }

  @override
  bool upsertConnection(Connection connection) {
    _connectionsById[connection.id] = connection;
    _connectionsByName[connection.name] = connection;
    int index = _myContacts.indexWhere((c) => c.id == connection.id);
    if (index >= 0) {
      _myContacts.removeAt(index);
    }
    switch (connection.status) {
      case ConnectionStatus.youFollowThem:
      case ConnectionStatus.theyFollowYou:
      case ConnectionStatus.mutual:
        if (index > 0) {
          _myContacts.insert(index, connection);
        } else {
          _myContacts.add(connection);
        }
        break;
      default:
        break;
    }

    return true;
  }

  @override
  List<Connection> getMyContacts() {
    return _myContacts;
  }

  @override
  Result<Connection, ExecError> getById(String id) {
    final result = _connectionsById[id];
    if (result == null) {
      return Result.error(ExecError(
        type: ErrorType.notFound,
        message: '$id not found',
      ));
    }

    return Result.ok(result);
  }

  @override
  Result<Connection, ExecError> getByName(String name) {
    final result = _connectionsByName[name];
    if (result == null) {
      return Result.error(ExecError(
        type: ErrorType.notFound,
        message: '$name not found',
      ));
    }

    return Result.ok(result);
  }

  @override
  Result<Connection, ExecError> getByHandle(String handle) {
    final result =
        _connectionsById.values.where((c) => c.handle == handle).toList();
    if (result.isEmpty) {
      return Result.error(ExecError(
        type: ErrorType.notFound,
        message: '$handle not found',
      ));
    }

    return Result.ok(result.first);
  }
}
