package liquibase.serializer.core.formattedsql;

import liquibase.change.Change;
import liquibase.changelog.ChangeLogChild;
import liquibase.changelog.ChangeSet;
import liquibase.configuration.GlobalConfiguration;
import liquibase.configuration.LiquibaseConfiguration;
import liquibase.database.Database;
import liquibase.database.DatabaseFactory;
import liquibase.exception.UnexpectedLiquibaseException;
import liquibase.serializer.ChangeLogSerializer;
import liquibase.serializer.LiquibaseSerializable;
import liquibase.sql.Sql;
import liquibase.sqlgenerator.SqlGeneratorFactory;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class FormattedSqlChangeLogSerializer  implements ChangeLogSerializer {

    private static Pattern fileNamePatter = Pattern.compile(".*\\.(\\w+)\\.sql");

    @Override
    public String[] getValidFileExtensions() {
        return new String[] {
                "sql"
        };
    }

    @Override
    public String serialize(LiquibaseSerializable object, boolean pretty) {
        if (object instanceof ChangeSet) {
            StringBuilder builder = new StringBuilder();

            ChangeSet changeSet = (ChangeSet) object;
            Database database = getTargetDatabase(changeSet);

            String author = (changeSet.getAuthor()).replaceAll("\\s+", "_");
            author = author.replace("_(generated)","");

            builder.append("--changeset ").append(author).append(":").append(changeSet.getId()).append("\n");
            for (Change change : changeSet.getChanges()) {
                Sql[] sqls = SqlGeneratorFactory.getInstance().generateSql(change.generateStatements(database), database);
                if (sqls != null) {
                    for (Sql sql : sqls) {
                        builder.append(sql.toSql()).append(sql.getEndDelimiter()).append("\n");
                    }
                }
            }

            return builder.toString();
        } else {
            throw new UnexpectedLiquibaseException("Cannot serialize object type: "+object.getClass().getName());
        }
    }

    protected Database getTargetDatabase(ChangeSet changeSet) {
        String filePath = changeSet.getFilePath();
        if (filePath == null) {
            throw new UnexpectedLiquibaseException("You must specify the changelog file name as filename.DB_TYPE.sql. Example: changelog.mysql.sql");
        }
        Matcher matcher = fileNamePatter.matcher(filePath);
        if (!matcher.matches()) {
            throw new UnexpectedLiquibaseException("Serializing changelog as sql requires a file name in the format *.databaseType.sql. Example: changelog.h2.sql. Passed: "+filePath);
        }
        String shortName = matcher.replaceFirst("$1");

        Database database = DatabaseFactory.getInstance().getDatabase(shortName);

        if (database == null) {
            throw new UnexpectedLiquibaseException("Serializing changelog as sql requires a file name in the format *.databaseType.sql. Example: changelog.h2.sql. Unknown databaes type: "+shortName);
        }

        return database;
    }

    @Override
    public <T extends ChangeLogChild> void write(List<T> children, OutputStream out) throws IOException {
        StringBuilder builder = new StringBuilder();
        builder.append("--liquibase formatted sql\n\n");

        for (T child : children) {
            builder.append(serialize(child, true));
            builder.append("\n");
        }

        out.write(builder.toString().getBytes(LiquibaseConfiguration.getInstance().getConfiguration(GlobalConfiguration.class).getOutputEncoding()));

    }

    @Override
    public void append(ChangeSet changeSet, File changeLogFile) throws IOException {

    }

    @Override
    public int getPriority() {
        return PRIORITY_DEFAULT;
    }
}

@@ -42,16 +42,40 @@
 edsql;%0A%0A
+import liquibase.Scope;%0A
 import l
@@ -418,24 +418,57 @@
 eException;%0A
+import liquibase.logging.Logger;%0A
 import liqui
@@ -957,24 +957,131 @@
 +)%5C%5C.sql%22);%0A
+    private static Logger logger = Scope.getCurrentScope().getLog(FormattedSqlChangeLogSerializer.class);%0A%0A
 %0A    @Overri
@@ -3109,32 +3109,406 @@
 base == null) %7B%0A
+            List%3CDatabase%3E databases = DatabaseFactory.getInstance().getImplementedDatabases();%0A            StringBuilder availableDbs = new StringBuilder();%0A            availableDbs.append(%22Available database short names for serialization:%5Cn%22);%0A            for (Database db : databases) %7B%0A                availableDbs.append(%22  %22 + db.getShortName() + %22%5Cn%22);%0A            %7D%0A
             thro
@@ -3668,18 +3668,18 @@
 n databa
-e
 s
+e
  type: %22
@@ -3688,16 +3688,37 @@
 hortName
+ +%22%5Cn%22 + availableDbs
 );%0A     

Improve error message when generating a changelog in sql format and the database type is unrecognized