package ameba.db.migration;

import ameba.core.Application;
import ameba.db.DataSourceManager;
import ameba.db.migration.resources.MigrationResource;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.jersey.server.ContainerRequest;

import javax.annotation.Priority;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.PreMatching;
import javax.ws.rs.core.Form;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import java.io.IOException;
import java.util.Map;

/**
 * <p>MigrationFilter class.</p>
 *
 * @author icode
 *
 */
@PreMatching
@Priority(Integer.MIN_VALUE)
@Singleton
public class MigrationFilter implements ContainerRequestFilter {
    private static final String FAVICON_ICO = "favicon.ico";
    private boolean ran = false;
    @Inject
    private Application.Mode mode;
    @Inject
    private Application application;
    @Inject
    private ServiceLocator locator;
    @Inject
    private MigrationResource resource;

    /**
     * {@inheritDoc}
     */
    @Override
    public void filter(ContainerRequestContext req) throws IOException {
        if (!ran) {
            UriInfo uriInfo = req.getUriInfo();
            String path = uriInfo.getPath();
            if (path.equals(FAVICON_ICO) || path.endsWith("/" + FAVICON_ICO)) {
                return;
            }
            path = "/" + path;
            String migrationUri = MigrationResource.MIGRATION_BASE_URI + MigrationFeature.getMigrationId();
            String repairUri = migrationUri + "/repair";

            Map<String, Object> properties = application.getProperties();
            Map failMigrations = resource.getFailMigrations();

            if (!mode.isDev() && (path.equals(migrationUri) || path.equals(repairUri))
                    && HttpMethod.GET.equals(req.getMethod())
                    && failMigrations != null && !failMigrations.isEmpty()) {
                repairView(req);
                return;
            } else if (path.equals(migrationUri) && HttpMethod.POST.equals(req.getMethod())) {
                String desc = ((ContainerRequest) req).readEntity(Form.class).asMap().getFirst("description");
                req.abortWith(
                        Response.ok().entity(resource.migrate(desc, MigrationFeature.getMigrationId())).build()
                );
                return;
            } else if (path.equals(repairUri) && HttpMethod.POST.equals(req.getMethod())) {
                req.abortWith(
                        Response.ok().entity(resource.repair(MigrationFeature.getMigrationId())).build()
                );
                return;
            } else if (!mode.isDev() && path.equals(migrationUri) && HttpMethod.GET.equals(req.getMethod())) {
                migrateView(req);
                return;
            } else if (!mode.isDev() && path.equals(repairUri) && HttpMethod.GET.equals(req.getMethod())) {
                repairView(req);
                return;
            }

            if (mode.isDev()) {
                if (HttpMethod.GET.equals(req.getMethod())
                        && failMigrations != null && !failMigrations.isEmpty()) {
                    repairView(req);
                    return;
                }
                for (String dbName : DataSourceManager.getDataSourceNames()) {
                    if (!"false".equals(properties.get("db." + dbName + ".migration.enabled"))) {
                        Migration migration = locator.getService(Migration.class, dbName);
                        if (migration.hasChanged()) {
                            migrateView(req);
                            return;
                        }
                    }
                }
            } else {
                boolean change = false;
                for (String dbName : DataSourceManager.getDataSourceNames()) {
                    if (!"false".equals(properties.get("db." + dbName + ".migration.enabled"))) {
                        Migration migration = locator.getService(Migration.class, dbName);
                        if (migration.hasChanged()) {
                            change = true;
                            break;
                        }
                    }
                }
                if (!change) {
                    if (failMigrations != null && !failMigrations.isEmpty()) {
                        change = true;
                    }
                }
                if (!change) {
                    ran = true;
                }
            }
        }
    }

    private void repairView(ContainerRequestContext req) {
        req.abortWith(
                Response.serverError()
                        .entity(resource.repairView(MigrationFeature.getMigrationId()))
                        .type(MediaType.TEXT_HTML_TYPE)
                        .build()
        );
    }

    private void migrateView(ContainerRequestContext req) {
        req.abortWith(Response.fromResponse(
                resource.migrateView(MigrationFeature.getMigrationId())
        ).status(500).build());
    }
}