package be.iminds.ilabt.jfed.rspec.rspec_source;

import be.iminds.ilabt.jfed.rspec.model.*;
import org.slf4j.LoggerFactory;
import org.slf4j.Marker;
import org.slf4j.MarkerFactory;

import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;

/**
 * RequestRspecSource
 */
public class ManifestRspecSource extends RspecSource {
    private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(ManifestRspecSource.class);
    public static final Marker JOIN_MANIFEST_RSPEC_MARKER = MarkerFactory.getMarker("joinManifestRspecs");

    public ManifestRspecSource(StringRspec stringRspec) {
        super(stringRspec);
    }

    public ManifestRspecSource(ModelRspec modelRspec) {
        super(modelRspec);
    }

    /**
     * Determines automatically to use raw XML, or ModelRspec
     */
    public ManifestRspecSource(String xmlRspecString, ModelRspecType modelRspecType) {
        super(xmlRspecString, modelRspecType);
    }

    @Override
    protected String modelToString(ModelRspec modelRspec, ModelRspec.RequestRspecSpecialCases rspecSpecialCase) {
        return modelRspec.toGeni3Rspec(rspecSpecialCase);
    }

    //TODO: fall back to parsing string itself, instead of using ModelRspec
    @Nullable
    public List<BasicStringRspec.LoginService> getNodeLoginInfo(RspecNode requestNode) {
        String nodeId = requestNode.getUniqueId();
        ModelRspec mRSpec = getModelRspec();
        if (mRSpec == null) return getNodeLoginInfoWithoutModel(requestNode);
        RspecNode manifestRspecNode = mRSpec.getNodeByUniqueId(nodeId);

        if(manifestRspecNode == null){
           LOG.warn("Could not find node with uniqueId {}. Doing fallback to clientId", requestNode.getUniqueId());
            manifestRspecNode = mRSpec.getNodeByClientId(requestNode.getClientId());
        }

        if (manifestRspecNode == null || manifestRspecNode.getLoginServices().size() == 0) {
            //fall back to getNodeLoginInfoWithoutModel(requestNode)
            //But if that works, there is a bug in the getNodeByUniqueId code
            List<BasicStringRspec.LoginService> res = getNodeLoginInfoWithoutModel(requestNode);
            if (res != null && !res.isEmpty()) {
                LOG.warn("Possible jFed bug. Could not find login info for " +
                        "RspecNode{id="+ requestNode.getUniqueId()+
                        " clientId="+requestNode.getClientId()+
                        " compId="+requestNode.getComponentId()+
                        " compManId="+requestNode.getComponentManagerId()+"} " +
                        "but did find it using BasicStringRspec#findNodeLoginInfo: "+res+"  rspec="+getRspecXmlString()
                );
                return res;
            }
        }

        if (manifestRspecNode == null || manifestRspecNode.getLoginServices().size() == 0)
            return null;

        List<BasicStringRspec.LoginService> res = new ArrayList<>();
        for (LoginService rspecLoginService : manifestRspecNode.getLoginServices()) {
            BasicStringRspec.LoginService ls = new BasicStringRspec.LoginService(
                    rspecLoginService.getAuthentication(),
                    rspecLoginService.getHostname(),
                    Integer.parseInt(rspecLoginService.getPort()),
                    rspecLoginService.getUsername(),
                    null/* TODO: process proxy info from manifestRspecNode and use it here */
                    );
            res.add(ls);
        }

        return res;
    }


    /*
     * helper to only extract login info, without full parsing and thus ModelRspec
     * This was originally in StringRspec, but does not belong there as it uses the RspecNode class etc.
     */
    public List<BasicStringRspec.LoginService> getNodeLoginInfoWithoutModel(RspecNode requestNode) {
        return getStringRspec().findNodeLoginInfoByUniqueId(requestNode.getUniqueId());
    }
}
