package in.cdac.medinfo.csnotk.csnolib.ecl.constraint;

import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.snomed.langauges.ecl.domain.refinement.EclAttribute;

import java.io.IOException;
import java.util.Collections;
import java.util.Set;

//Logs
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import in.cdac.medinfo.csnotk.csnolib.util.LoggerUtility;
//Changes
import in.cdac.medinfo.csnotk.csnolib.agents.LuceneSearchAgent;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.CONCEPTID;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.TYPE;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.DESTINATION;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.ACTIVE;
import in.cdac.medinfo.csnotk.csnolib.model.Concept;
import java.util.List;
import org.apache.lucene.search.TermInSetQuery;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.util.BytesRef;
import java.util.stream.Collectors;
import static com.google.common.collect.Sets.newHashSet;
import in.cdac.medinfo.csnotk.csnolib.ecl.util.throwEclFeatureNotSupportedException;

public class SEclAttribute extends EclAttribute implements SConstraint {

    private LuceneSearchAgent searchAgent = new LuceneSearchAgent();

    @Override
    public Set<Concept> addQuery() {
        Logger logger = LogManager.getLogger(SEclAttribute.class);
        if (cardinalityMin != 1 || cardinalityMax != null) {
            throw new throwEclFeatureNotSupportedException("Attribute cardinality");
        }
        if (reverse) {
            throw new throwEclFeatureNotSupportedException("Reverse flag");
        }

        SSubExpressionConstraint attributeName = (SSubExpressionConstraint) getAttributeName();
        Set<Concept> attributeTypes = null;
        if (!attributeName.isWildcard()) {
            attributeTypes = attributeName.addQuery();
        }

        if (getExpressionComparisonOperator() == null) {
            throw new IllegalArgumentException("ECL comparison operators other than the expression comparison operator are 'not' supported by this implementation.");
        }
        boolean equals = getExpressionComparisonOperator().equals("=");
        SSubExpressionConstraint value = (SSubExpressionConstraint) getValue();
        Set<Concept> valueIds = null;
        if (!value.isWildcard()) {
            valueIds = value.addQuery();
        }
        try {
            BooleanQuery.Builder builder = new BooleanQuery.Builder();
            //Building Attribute query
            if(attributeTypes != null && !attributeTypes.isEmpty())
                builder.add(termsQueryFromConcepts(TYPE + CONCEPTID, attributeTypes), BooleanClause.Occur.MUST);
            //Building Attribute - value query
            if(valueIds != null && !valueIds.isEmpty())
                builder.add(termsQueryFromConcepts(DESTINATION + CONCEPTID, valueIds), equals ? BooleanClause.Occur.MUST : BooleanClause.Occur.MUST_NOT);
            //Must be Active
            builder.add(new TermQuery(new Term(ACTIVE, "1")),BooleanClause.Occur.MUST);
            return searchAgent.getConceptsFromQuery(builder);
        } catch (java.text.ParseException  | IOException  e) {
            logger.error(LoggerUtility.ConsoleWithException, LoggerUtility.createErrorMessage(e));
        }
        return Collections.emptySet();
    }

    //QueryHelper
    public static TermInSetQuery termsQueryFromConcepts(String fieldName, Set<Concept> values) {
        List<BytesRef> collect = values.stream().map(value -> new BytesRef(value.getId())).collect(Collectors.toList());
        return new TermInSetQuery(fieldName, collect);
    }

    public boolean isConcreteValueQuery() {
        return getNumericComparisonOperator() != null || getStringComparisonOperator() != null || getBooleanComparisonOperator() != null;
    }

    @Override
    public Set<String> getConceptIds() {
        Set<String> conceptIds = newHashSet();
        conceptIds.addAll(((SSubExpressionConstraint) attributeName).getConceptIds());
        if (!isConcreteValueQuery()) {
            conceptIds.addAll(((SSubExpressionConstraint) value).getConceptIds());
        }
        return conceptIds;
    }

}
