/*
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira
 */

package net.ranides.assira.credentials;

import net.ranides.assira.collection.prototype.GenericKey;
import net.ranides.assira.collection.prototype.GenericMap;
import net.ranides.assira.collection.query.CQuery;
import net.ranides.assira.io.uri.URIUtils;

import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * Simple, in-memory user store. Users are stored in ArrayList, all search operations run in linear time.
 *
 * This store is mutable. You can add users at any time.
 *
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 */
public class UserStoreList implements UserStore {

    private final List<GenericMap<String>> list;

    /**
     * Creates new empty store
     */
    public UserStoreList() {
        list = new ArrayList<>();
    }


    /**
     * Finds users matching URI. Extracts from URI host & port and compares it with fields:
     *  - UserProperty.HOST
     *  - UserProperty.PORT
     *
     * @param uri uri
     * @return users
     */
    @Override
    public CQuery<GenericMap<String>> find(URI uri) {
        return CQuery.from(list)
            .filter(u -> matchName(u, URIUtils.getLogin(uri).orElse(null)))
            .filter(u -> matchHost(u, uri.getHost()))
            .filter(u -> matchPort(u, uri.getPort()))
            .filter(u -> matchURI(u, uri));
    }

    private boolean matchName(GenericMap<String> user, String name) {
        return name == null || user.containsValue(UserProperty.ACCOUNT, name);
    }

    private boolean matchHost(GenericMap<String> user, String value) {
        return value==null || user.getOptional(UserProperty.HOST).map(value::equals).orElse(true);
    }

    private boolean matchPort(GenericMap<String> user, Integer value) {
        return value==-1 || user.getOptional(UserProperty.PORT).map(value::equals).orElse(true);
    }

    private boolean matchURI(GenericMap<String> user, URI uri) {
        return user.getOptional(UserProperty.URI).map(p -> p.test(uri)).orElse(true);
    }

    /**
     * Finds users with UserProperty.ACCOUNT equal to specified query value.
     *
     * @param query query
     * @return users
     */
    @Override
    public CQuery<GenericMap<String>> find(String query) {
        return CQuery.from(list).filter(u -> matchName(u, query));
    }

    /**
     * Inserts user into userstore. Below properties are used in queries:
     *  - UserProperty.ACCOUNT
     *  - UserProperty.HOST
     *  - UserProperty.PORT
     *
     * @param user user
     * @return this
     */
    public UserStoreList add(GenericMap<String> user) {
        list.add(user);
        return this;
    }

}
