/*
 * Copyright (C) Lightbend Inc. <https://www.lightbend.com>
 */
package play.db.ebean;

import io.ebean.EbeanServer;
import io.ebean.EbeanServerFactory;
import io.ebeaninternal.dbmigration.model.CurrentModel;
import io.ebeaninternal.api.SpiEbeanServer;
import play.Environment;
import play.api.db.evolutions.DynamicEvolutions;
import play.inject.ApplicationLifecycle;

import javax.inject.Inject;
import javax.inject.Singleton;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

/**
 * A Play module that automatically manages Ebean configuration.
 */
@Singleton
public class EbeanDynamicEvolutions extends DynamicEvolutions {

    private final EbeanConfig config;
    private final Environment environment;

    private final Map<String, EbeanServer> servers = new HashMap<>();

    @Inject
    public EbeanDynamicEvolutions(EbeanConfig config, Environment environment, ApplicationLifecycle lifecycle) {
        this.config = config;
        this.environment = environment;
        start();
        lifecycle.addStopHook(() -> {
            servers.forEach((database, server) -> server.shutdown(false, false));
            return CompletableFuture.completedFuture(null);
        });
    }

    /**
     * Initialise the Ebean servers.
     */
    public void start() {
        config.serverConfigs().forEach((key, serverConfig) -> servers.put(key, EbeanServerFactory.create(serverConfig)));
    }

    /**
     * Generate evolutions.
     */
    @Override
    public void create() {
        if (!environment.isProd()) {
            config.serverConfigs().forEach((key, serverConfig) -> {
                String evolutionScript = generateEvolutionScript(servers.get(key));
                if (evolutionScript != null) {
                    File evolutions = environment.getFile("conf/evolutions/" + key + "/1.sql");
                    try {
                        String content = "";
                        if (evolutions.exists()) {
                            content = new String(Files.readAllBytes(evolutions.toPath()), "utf-8");
                        }

                        if (content.isEmpty() || content.startsWith("# --- Created by Ebean DDL")) {
                            environment.getFile("conf/evolutions/" + key).mkdirs();
                            if (!content.equals(evolutionScript)) {
                                Files.write(evolutions.toPath(), evolutionScript.getBytes("utf-8"));
                            }
                        }
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            });
        }
    }

    /**
     * Helper method that generates the required evolution to properly run Ebean.
     *
     * @param server the EbeanServer.
     * @return the complete migration generated by Ebean
     */
    public static String generateEvolutionScript(EbeanServer server) {
        try {
            SpiEbeanServer spiServer = (SpiEbeanServer) server;
            CurrentModel ddl = new CurrentModel(spiServer);

            String ups = ddl.getCreateDdl();
            String downs = ddl.getDropAllDdl();

            if (ups == null || ups.trim().isEmpty()) {
                return null;
            }

            return
                "# --- Created by Ebean DDL\r\n" +
                "# To stop Ebean DDL generation, remove this comment and start using Evolutions\r\n" +
                "\r\n" +
                "# --- !Ups\r\n" +
                "\r\n" +
                ups +
                "\r\n" +
                "# --- !Downs\r\n" +
                "\r\n" +
                downs;
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        }
    }
}