package com.atlassian.stash.internal.backup.liquibase.processors;

import com.google.common.base.CharMatcher;
import liquibase.change.ColumnConfig;
import liquibase.change.core.DeleteDataChange;
import liquibase.change.core.InsertDataChange;
import liquibase.changelog.ChangeSet;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * {@link ChangeSetProcessor} that filters out null characters (U+0000) from the changeset data.
 *
 * @see com.atlassian.stash.internal.backup.liquibase.ColumnParsingContext#asColumnConfig(com.atlassian.stash.internal.backup.liquibase.xml.XmlEncoder)
 */
public class NullFilteringChangeSetProcessor implements ChangeSetProcessor {

    private static final Logger log = LoggerFactory.getLogger(NullFilteringChangeSetProcessor.class);

    private static final CharMatcher NULL_CHAR_MATCHER = CharMatcher.is('\u0000');

    @Override
    public void onChangesetBegin(ChangeSet changeSet) {
    }

    @Override
    public void onChangesetContent(InsertDataChange change) {
        for (ColumnConfig columnConfig : change.getColumns()) {
            String value = columnConfig.getValue();
            if (value != null && NULL_CHAR_MATCHER.matchesAnyOf(value)) {
                columnConfig.setValue(NULL_CHAR_MATCHER.removeFrom(value));
                log.warn("Removed the null characters (U+0000) on the following row on the table '{}':\n" +
                         " - id: '{}'\n - column: '{}'\n - value: '{}'", change.getTableName(), findRowId(change),
                        columnConfig.getName(), StringUtils.abbreviate(columnConfig.getValue(), 50));
            }
        }
    }

    @Override
    public void onChangesetContent(DeleteDataChange change) {
    }

    @Override
    public void onChangesetComplete(ChangeSet changeSet) {
    }

    private String findRowId(InsertDataChange change) {
        for (ColumnConfig column : change.getColumns()) {
            if (column.getName().equalsIgnoreCase("id")) {
                if (column.getValueNumeric() != null) {
                    return column.getValueNumeric().toString();
                } else if (column.getValueObject() != null) {
                    return column.getValueObject().toString();
                } else if (column.getValue() != null) {
                    return column.getValue();
                }
            }
        }
        return "?";
    }

}
