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

import com.atlassian.stash.internal.backup.liquibase.xml.DelegatingXmlStreamWriter;
import com.atlassian.stash.internal.backup.liquibase.xml.XmlEncoder;
import liquibase.serializer.core.xml.XMLChangeLogSerializer;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;

import static com.atlassian.stash.internal.backup.liquibase.LiquibaseConstants.*;

/**
 * This implementation creates elements of the output XML document directly. You might think that we could use
 * Liquibase's {@link XMLChangeLogSerializer} class, but it uses DOM-based document construction, which carries with it
 * an overhead that we avoid by employing a purely streaming approach.
 */
class DefaultLiquibaseXmlWriter extends DelegatingXmlStreamWriter<XMLStreamWriter> implements LiquibaseXmlWriter {
    private final ChangeSetIdGenerator idGenerator;
    private final String changeSetAuthor;
    private final XmlEncoder xmlEncoder;

    DefaultLiquibaseXmlWriter(XMLStreamWriter delegate, ChangeSetIdGenerator idGenerator, String changeSetAuthor,
                              XmlEncoder xmlEncoder) {
        super(delegate);
        this.idGenerator = idGenerator;
        this.changeSetAuthor = changeSetAuthor;
        this.xmlEncoder = xmlEncoder;
    }

    @Override
    public void writeDatabaseChangeLogStartElement() throws XMLStreamException {
        writeStartElement(DATABASE_CHANGE_LOG);
    }

    @Override
    public void writeChangeSetToDeleteRowsFromTable(@Nonnull String tableName) throws XMLStreamException {
        writeChangeSetStartElement(tableName);
        writeDeleteAll(tableName);
        writeEndElement();
    }

    @Override
    public void writeChangeSetStartElement(@Nonnull String tableName) throws XMLStreamException {
        writeStartElement(CHANGE_SET);
        writeAttribute(CHANGE_SET_ID, idGenerator.next(tableName));
        writeAttribute(CHANGE_SET_AUTHOR, changeSetAuthor);
    }

    private void writeDeleteAll(String tableName) throws XMLStreamException {
        writeStartElement(DELETE);
        writeAttribute(TABLE_NAME, tableName.toLowerCase());
        writeEmptyElement(WHERE);
        writeEndElement();
    }

    @Override
    public void writeColumn(@Nonnull String columnName, @Nullable Object value) throws XMLStreamException {
        if (value == null) {
            return;
        }
        writeStartElement(COLUMN);
        writeAttribute(COLUMN_NAME, columnName);
        ColumnSerialisationType columnSerialisationType = ColumnSerialisationType.ofValue(value);
        writeAttribute(COLUMN_TYPE, columnSerialisationType.toString());
        writeCData(xmlEncoder.encodeForCdata(columnSerialisationType.toColumnString(value)));
        writeEndElement();
    }

    ChangeSetIdGenerator getIdGenerator() {
        return idGenerator;
    }

    String getChangeSetAuthor() {
        return changeSetAuthor;
    }
}
