package xyz.fabiano.spring.localstack.autoconfigure;


import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextStoppedEvent;
import org.springframework.context.event.EventListener;
import xyz.fabiano.spring.localstack.LocalstackDockerBuilder;
import xyz.fabiano.spring.localstack.LocalstackService;
import xyz.fabiano.spring.localstack.legacy.LocalstackDocker;
import xyz.fabiano.spring.localstack.support.AmazonAsyncDockerClientsHolder;
import xyz.fabiano.spring.localstack.support.AmazonClientsHolder;
import xyz.fabiano.spring.localstack.support.AmazonDockerClientsHolder;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Configuration
@ConditionalOnProperty("spring.localstack.enabled")
public class LocalstackAutoConfiguration {

    private LocalstackDocker localstackDocker;

    private final boolean asyncClientsEnabled;
    private final boolean pullNewImage;
    private final boolean randomPorts;
    private final String externalHost;
    private final String services;
    private final boolean autoRemove;
    private final String extraOptions;
    private final String version;
    private final String region;

    public LocalstackAutoConfiguration(
        @Value("${spring.localstack.async-clients.enabled:false}") boolean asyncClientsEnabled,
        @Value("${spring.localstack.pull-new-image:false}") boolean pullNewImage,
        @Value("${spring.localstack.random-ports:true}") boolean randomPorts,
        @Value("${spring.localstack.external-host:localhost}") String externalHost,
        @Value("${spring.localstack.services:}") String services,
        @Value("${spring.localstack.auto-remove:true}") boolean autoRemove,
        @Value("${spring.localstack.extra-options:}") String extraOptions,
        @Value("${spring.localstack.version:latest}") String version,
        @Value("${spring.localstack.region:us-east-1}") String region) {
        this.asyncClientsEnabled = asyncClientsEnabled;
        this.pullNewImage = pullNewImage;
        this.randomPorts = randomPorts;
        this.externalHost = externalHost;
        this.services = services;
        this.autoRemove = autoRemove;
        this.extraOptions = extraOptions;
        this.version = version;
        this.region = region;
    }

    @Bean
    public LocalstackDocker localstackDocker() {
        localstackDocker = new LocalstackDockerBuilder()
            .pullingNewImages(pullNewImage)
            .withRandomPorts(randomPorts)
            .withExternalHost(externalHost)
            .disableCBOR()
            .withServices(services())
            .withOptions(options())
            .withVersion(version)
            .withRegion(region)
            .build();

        localstackDocker.startup();
        return localstackDocker;
    }

    public List<LocalstackService> services() {
        return Stream.of(services.split(","))
            .filter(s -> !StringUtils.isBlank(s))
            .map(s -> LocalstackService.valueOf(s.toUpperCase()))
            .collect(Collectors.toList());
    }

    private List<String> options() {
        List<String> options = new ArrayList<>();
        if (autoRemove) {
            options.add("--rm");
        }

        if (StringUtils.isNotBlank(extraOptions)) {
            options.addAll(Stream.of(extraOptions.split(","))
                .filter(s -> !StringUtils.isBlank(s))
                .collect(Collectors.toList()));
        }

        return options;
    }

    @Bean
    public AmazonClientsHolder amazonClientsHolder() {
        if (asyncClientsEnabled) {
            return new AmazonAsyncDockerClientsHolder(localstackDocker());
        } else {
            return new AmazonDockerClientsHolder(localstackDocker());
        }
    }

    @EventListener({ContextStoppedEvent.class, ContextClosedEvent.class})
    public void cleanContext() {
        if (localstackDocker != null) {
            localstackDocker.stop();
        }
    }
}