/*
 * @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.generic.Ref;
import net.ranides.assira.index.IQueryMap;
import net.ranides.assira.index.IQueryMapTree;
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;

/**
 * Simple, in-memory user store.
 * It supports fast search by URI, matching UserProperty.HOST & UserProperty.PORT.
 * It supports fast search by name, matching UserProperty.ACCOUNT.
 *
 * This store is mutable. You can add users at any time.
 *
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 */
public class UserStoreIndex implements UserStore {

    private final IQueryMap<GenericMap<String>> tree;

    /**
     * Creates new empty store
     */
    public UserStoreIndex() {
        tree = new IQueryMapTree<>();
        index(UserProperty.ACCOUNT);
        index(UserProperty.HOST);
        index(UserProperty.PORT);
    }

    private <T> void index(GenericKey<String, T> key) {
        tree.index(key.id(), key);
    }

    /**
     * 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) {
        Ref<String> login = URIUtils.getLogin(uri);

        if(login.isPresent()) {
            return tree.find()
                .eq(UserProperty.ACCOUNT, login.get())
                .eq(UserProperty.HOST, true, uri.getHost())
                .eq(UserProperty.PORT, true, uri.getPort())
                .filter(u -> urimatch(u, uri));
        } else {
            return tree.find()
                .eq(UserProperty.HOST, true, uri.getHost())
                .eq(UserProperty.PORT, true, uri.getPort())
                .filter(u -> urimatch(u, uri));
        }
    }

    private boolean urimatch(GenericMap<String> u, URI uri) {
        return u.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 tree.find().eq(UserProperty.ACCOUNT, query);
    }

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

}
