// Copyright (c) 2020 UWB & LESP.
// The UWB & LESP license this file to you under the MIT license.

package cz.senslog.connector.fetch.api;

import cz.senslog.connector.tools.interceptor.AbstractMethodInterceptor;
import cz.senslog.connector.model.api.AbstractModel;
import cz.senslog.connector.model.api.ProxySessionModel;

import java.util.Optional;

import static cz.senslog.connector.tools.util.MethodExplorer.loadMethod;
import static java.util.Optional.*;

/**
 * The {@link ExecutableFetcher} may require additional preprocessing for its fetch task,
 *      e.g., get an initial timestamp as a latest on of persisted data
 * For such task is responsible the method {@link #preProcessing(Object[])}, which is executed before {@link ConnectorFetcher#fetch(Optional)}.
 * The class includes session as an inherited instance of {@link ProxySessionModel}.
 * One the session is created, it lives through the entire time and can be used to store data.
 * The same logic is applied to the method {@link #postProcessing(Object)}, which is executed after {@link  ConnectorFetcher#fetch(Optional)}.
 *
 * @param <S> data model for the session as an inherited class of the {@link ProxySessionModel}
 * @param <T> model representing the fetched data as an inherited class of the {@link AbstractModel}
 *
 * @author Lukas Cerny
 */
public abstract class FetchProxySession<S extends ProxySessionModel, T extends AbstractModel> extends AbstractMethodInterceptor {

    private Optional<S> optionalSession;

    protected FetchProxySession(ConnectorFetcher<S, T> fetcher) {
        super(fetcher, loadMethod(ConnectorFetcher.class, "fetch", Optional.class));
        this.optionalSession = Optional.empty();
    }

    /**
     * Method to override that may include logic before the fetch is executed.
     * @param previousSession session of the previous run
     * @return session
     */
    protected abstract S preProcessing(Optional<S> previousSession);

    /**
     * Method to override that may include logic to transform fetched data.
     * @param model retrieved data after fetch
     * @param session current session of the ongoing run
     */
    protected abstract void postProcessing(Optional<T> model, Optional<S> session);

    protected ConnectorFetcher<S, T> getObject() {
        return (ConnectorFetcher<S, T>) super.getObject();
    }

    @Override
    public final Object[] preProcessing(Object[] methodParams) {
        S s = preProcessing(optionalSession);
        if (s.isActive()) {
            optionalSession = of(s);
            return new Object[]{optionalSession};
        } else {
            optionalSession = empty();
            return null;
        }
    }

    @Override
    public final Object postProcessing(Object returnObject) {
        Optional<T> returnOpt = ofNullable((T)returnObject);
        postProcessing(returnOpt, optionalSession);
        return returnObject;
    }
}
