/*-
 * #%L
 * JSQLParser library
 * %%
 * Copyright (C) 2004 - 2020 JSQLParser
 * %%
 * Dual licensed under GNU LGPL 2.1 or Apache License 2.0
 * #L%
 */
package net.sf.jsqlparser.util.validation.metadata;

import java.sql.SQLException;
import java.util.function.Consumer;

import net.sf.jsqlparser.util.validation.UnexpectedValidationException;
import net.sf.jsqlparser.util.validation.ValidationCapability;
import net.sf.jsqlparser.util.validation.ValidationContext;
import net.sf.jsqlparser.util.validation.ValidationException;

public interface DatabaseMetaDataValidation extends ValidationCapability {

    String NAME = "meta data";

    /**
     * @param context
     * @param errorConsumer
     * @throws ValidationException
     */
    @Override
    default void validate(ValidationContext context, Consumer<ValidationException> errorConsumer) {
        Named named = context.get(MetadataContext.named, Named.class);
        boolean checkForExists = context.get(MetadataContext.exists, Boolean.class);
        try {
            boolean exists = exists(named);
            if (exists ^ checkForExists) { // XOR
                errorConsumer.accept(getErrorMessage(named, checkForExists));
            }
        } catch (ValidationException ve) {
            errorConsumer.accept(ve);
        } catch (UnsupportedOperationException uoe) {
            errorConsumer.accept(new ValidationException(
                    "This Operation " + named.toString() + "  is not supported yet.", uoe));
        } catch (Exception e) {
            errorConsumer.accept(getUnexpectedErrorMessage(named, e));
        }
    }

    /**
     * @param named
     * @return <code>true</code>, if the object exists, <code>false</code> otherwise.
     * @throws ValidationException - on specific errors like {@link DatabaseException} on
     *         database-errors wrapping a {@link SQLException} or PersistenceException
     * @throws UnsupportedOperationException - if testing of given {@link NamedObject} is not
     *         supported.
     */
    boolean exists(Named named);

    /**
     * @param named
     * @param checkForExists
     * @return a new {@link ValidationException}
     */
    default ValidationException getErrorMessage(Named named, boolean checkForExists) {
        return toError(
                String.format("%s does %sexist.", named.getFqn(), checkForExists ? "not " : ""));
    }

    /**
     * @param named
     * @param cause
     * @return a new {@link ValidationException}
     */
    default ValidationException getUnexpectedErrorMessage(Named named, Exception cause) {
        return new UnexpectedValidationException(
                named.getFqn() + ": cannot validate " + named.getNamedObject() + "-name. detail: "
                        + cause.getMessage(),
                cause);
    }

    @Override
    default String getName() {
        return NAME;
    }

}
