import 'package:result_monad/result_monad.dart';

import '../../models/connection.dart';
import '../../models/exec_error.dart';
import '../../objectbox.g.dart';
import '../interfaces/connections_repo_intf.dart';
import '../memory/memory_connections_repo.dart';
import 'objectbox_cache.dart';

class ObjectBoxConnectionsRepo implements IConnectionsRepo {
  late final Box<Connection> box;
  final memCache = MemoryConnectionsRepo();

  ObjectBoxConnectionsRepo(ObjectBoxCache cache) {
    box = cache.store.box<Connection>();
  }

  @override
  void clear() {
    memCache.clear();
    box.removeAll();
  }

  @override
  Result<Connection, ExecError> getById(String id) {
    final result = memCache.getById(id);
    if (result.isSuccess) {
      return result;
    }

    return _getConnection(
      Connection_.id.equals(id),
    ).mapError(
      (error) => error.copy(
        message: "Can't find Connection for ID: $id",
      ),
    );
  }

  @override
  Result<Connection, ExecError> getByName(String name) {
    final result = memCache.getByName(name);
    if (result.isSuccess) {
      return result;
    }
    return _getConnection(
      Connection_.name.equals(name),
    ).mapError(
      (error) => error.copy(
        message: "Can't find Connection for name: $name",
      ),
    );
  }

  @override
  List<Connection> getKnownUsersByName(String name) {
    return box
        .query(
          Connection_.name.contains(name, caseSensitive: false) |
              Connection_.handle.contains(name, caseSensitive: false),
        )
        .build()
        .find();
  }

  @override
  Result<Connection, ExecError> getByHandle(String handle) {
    final result = memCache.getByHandle(handle);
    if (result.isSuccess) {
      return result;
    }
    return _getConnection(
      Connection_.handle.equals(handle),
    ).mapError(
      (error) => error.copy(
        message: "Can't find Connection for Handle: $handle",
      ),
    );
  }

  @override
  List<Connection> getMyContacts() {
    final connectedStatuses = [
      ConnectionStatus.youFollowThem.code,
      ConnectionStatus.theyFollowYou.code,
      ConnectionStatus.mutual.code,
    ];
    return box
        .query(Connection_.dbStatus.oneOf(connectedStatuses))
        .build()
        .find();
  }

  @override
  bool upsertConnection(Connection connection) {
    memCache.upsertConnection(connection);
    getById(connection.id).match(
      onSuccess: (existing) => box.putQueued(
        connection.copy(obId: existing.obId),
      ),
      onError: (_) => box.putQueued(connection),
    );
    return true;
  }

  Result<Connection, ExecError> _getConnection(Condition<Connection> query) {
    final connection = box.query(query).build().findFirst();
    if (connection == null) {
      return buildErrorResult(type: ErrorType.notFound);
    }

    memCache.upsertConnection(connection);
    return Result.ok(connection);
  }
}
