package org.protege.editor.owl.model.idrange;

import org.protege.editor.owl.model.entity.EntityCreationPreferences;
import org.protege.editor.owl.model.git.GitRepositoryManager;
import org.protege.editor.owl.model.user.DefaultUserNameProvider;
import org.protege.editor.owl.model.user.UserNamePreferencesManager;
import org.protege.editor.owl.model.user.UserNameProvider;
import org.protege.editor.owl.model.user.UserPreferences;
import org.semanticweb.owlapi.model.IRI;
import org.semanticweb.owlapi.vocab.OWLRDFVocabulary;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nonnull;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static com.google.common.base.Preconditions.checkNotNull;

/**
 * Matthew Horridge
 * Stanford Center for Biomedical Informatics Research
 * 2019-04-25
 */
public class IdPolicyEntityCreationPreferencesUpdater {

    private static final Pattern ID_PREFIX_PATTERN = Pattern.compile("(.+)([/#:])([A-Za-z0-9]+)_$");

    private final static Logger logger = LoggerFactory.getLogger(IdPolicyEntityCreationPreferencesUpdater.class);

    @Nonnull
    private final IdRangesPolicy policy;

    @Nonnull
    private final NoRangeForUserNameHandler noRangeForUserNameHandler;

    @Nonnull
    private final UserNameProvider userNameProvider;

    public IdPolicyEntityCreationPreferencesUpdater(@Nonnull IdRangesPolicy policy,
                                                    @Nonnull NoRangeForUserNameHandler noRangeForUserNameHandler,
                                                    @Nonnull UserNameProvider userNameProvider) {
        this.policy = checkNotNull(policy);
        this.noRangeForUserNameHandler = checkNotNull(noRangeForUserNameHandler);
        this.userNameProvider = checkNotNull(userNameProvider);
    }

    public void updatePreferences() {
        Matcher matcher = ID_PREFIX_PATTERN.matcher(policy.getIdPrefix());
        if(!matcher.matches()) {
            logger.warn("[IdRanges] Cannot process prefix {}", policy.getIdPrefix());
            return;
        }

        logger.info("[IdRanges] Setting id digit count to {}", policy.getIdDigitCount());
        EntityCreationPreferences.setAutoIDDigitCount(policy.getIdDigitCount());

        String baseIri = matcher.group(1);
        logger.info("[IdRanges] Setting prefix to {}", baseIri);
        EntityCreationPreferences.setDefaultBaseIRI(IRI.create(baseIri));
        EntityCreationPreferences.setUseDefaultBaseIRI(true);

        String separator = matcher.group(2);
        logger.info("[IdRanges] Setting separator to {}", separator);
        EntityCreationPreferences.setDefaultSeparator(separator);

        String protegePrefix = matcher.group(3) + "_";
        logger.info("[IdRanges] Setting entity prefix to {}", protegePrefix);
        EntityCreationPreferences.setPrefix(protegePrefix);
        EntityCreationPreferences.setSuffix("");

        logger.info("[IdRanges] Setting entity local name to auto-generated");
        EntityCreationPreferences.setFragmentAutoGenerated(true);


        logger.info("[IdRanges] Setting labelling property to rdfs:label");
        EntityCreationPreferences.setGenerateIDLabel(false);
        EntityCreationPreferences.setGenerateNameLabel(true);
        EntityCreationPreferences.setNameLabelIRI(OWLRDFVocabulary.RDFS_LABEL.getIRI());

        updateRangeForCurrentUserName();

    }


    private void updateRangeForCurrentUserName() {
        userNameProvider.getUserName().ifPresent(this::updateRangeForUser);
    }

    private void updateRangeForUser(@Nonnull String userName) {
        logger.info("[IdRanges] Current user name is {}", userName);
        Optional<UserIdRange> userIdRange = policy
                .getUserIdRanges()
                .stream()
                .filter(rng -> rng.getUserId().equalsIgnoreCase(userName))
                .findFirst();
        userIdRange.ifPresent(this::updateNumericRange);
        if(!userIdRange.isPresent()) {
            logger.info("[IdRanges] Could not find a matching user name");
            noRangeForUserNameHandler.handleNoRangeForUserName(userName, policy, this::updateNumericRange);
        }
    }

    private void updateNumericRange(UserIdRange idRange) {
        logger.info("[IdRanges] Updating id range for {}", idRange.getUserId());
        IdRange rng = idRange.getIdRange();
        updateNumericRange(rng);
    }

    private void updateNumericRange(IdRange rng) {
        int start = rng.getLowerBound();
        logger.info("[IdRanges] Setting id start to {}", start);
        EntityCreationPreferences.setAutoIDStart(start);
        int end = rng.getUpperBound();
        logger.info("[IdRanges] Setting id end to {}", end);
        EntityCreationPreferences.setAutoIDEnd(end);
        String name = String.format("%s_%d", policy.getIdPolicyFor(), start);
        logger.info("[IdRanges] Setting policy name to {}", name);
        EntityCreationPreferences.setPolicyRangeName(name);
    }


}
