package com.timgroup.eventstore.mysql;

import java.util.Collection;
import java.util.Optional;
import java.util.Properties;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.timgroup.eventstore.api.EventCategoryReader;
import com.timgroup.eventstore.api.EventReader;
import com.timgroup.eventstore.api.EventSource;
import com.timgroup.eventstore.api.EventStreamReader;
import com.timgroup.eventstore.api.EventStreamWriter;
import com.timgroup.eventstore.api.PositionCodec;
import com.timgroup.tucker.info.Component;
import com.timgroup.tucker.info.component.DatabaseConnectionComponent;
import com.typesafe.config.Config;
import org.slf4j.LoggerFactory;

import static java.util.Collections.singletonList;

public class BasicMysqlEventSource implements EventSource {
    private static final int DefaultBatchSize = 100000;

    private final ConnectionProvider connectionProvider;
    private final String tableName;
    private final int batchSize;
    private final String name;

    public BasicMysqlEventSource(ConnectionProvider connectionProvider, String tableName, int batchSize, String name) {
        this.connectionProvider = connectionProvider;
        this.tableName = tableName;
        this.batchSize = batchSize;
        this.name = name;
    }

    public BasicMysqlEventSource(ConnectionProvider connectionProvider, String tableName, int batchSize) {
        this(connectionProvider, tableName, batchSize, "EventStore");
    }

    public BasicMysqlEventSource(ConnectionProvider connectionProvider, String tableName) {
        this(connectionProvider, tableName, DefaultBatchSize);
    }

    @Override
    public EventReader readAll() {
        return new BasicMysqlEventReader(connectionProvider, tableName, batchSize);
    }

    @Override
    public EventCategoryReader readCategory() {
        return new BasicMysqlEventCategoryReader(connectionProvider, tableName, batchSize);
    }

    @Override
    public EventStreamReader readStream() {
        return new BasicMysqlEventStreamReader(connectionProvider, tableName, batchSize);
    }

    @Override
    public EventStreamWriter writeStream() {
        return new BasicMysqlEventStreamWriter(connectionProvider, tableName);
    }

    @Override
    public PositionCodec positionCodec() {
        return BasicMysqlEventStorePosition.CODEC;
    }

    @Override
    public Collection<Component> monitoring() {
        String id = "EventStore-" + this.name;
        String label = "EventStore (" + this.name + ")";
        Component databaseConnection = new DatabaseConnectionComponent(id, label, connectionProvider::getConnection);
        if (databaseConnectionInfo().isPresent()) {
            String prefix = databaseConnectionInfo().get() + ": ";
            databaseConnection = databaseConnection.mapValue(v -> prefix + v);
        }
        return singletonList(databaseConnection);
    }

    protected Optional<String> databaseConnectionInfo() {
        return Optional.empty();
    }



    public static PooledMysqlEventSource pooledMasterDbEventSource(Config config, String tableName, String name) {
        return pooledMasterDbEventSource(config, tableName, name, DefaultBatchSize);
    }

    public static PooledMysqlEventSource pooledMasterDbEventSource(Config config, String tableName, String name, int batchSize) {
        return pooledMasterDbEventSource(StacksConfiguredDataSource.pooledMasterDb(config), tableName, name, batchSize);
    }

    public static PooledMysqlEventSource pooledMasterDbEventSource(Properties properties, String configPrefix, String tableName, String name) {
        return pooledMasterDbEventSource(properties, configPrefix, tableName, name, DefaultBatchSize);
    }

    public static PooledMysqlEventSource pooledMasterDbEventSource(Properties properties, String configPrefix, String tableName, String name, int batchSize) {
        return pooledMasterDbEventSource(StacksConfiguredDataSource.pooledMasterDb(properties, configPrefix), tableName, name, batchSize);
    }

    private static PooledMysqlEventSource pooledMasterDbEventSource(ComboPooledDataSource dataSource, String tableName, String name, int batchSize) {
        try {
            new BasicMysqlEventStoreSetup(dataSource::getConnection, tableName).lazyCreate();
        } catch (Exception e) {
            LoggerFactory.getLogger(BasicMysqlEventSource.class).warn("Failed to ensure ES scheme is created", e);
        }

        return new PooledMysqlEventSource(dataSource, tableName, batchSize, name);
    }

    public static final class PooledMysqlEventSource extends BasicMysqlEventSource implements AutoCloseable {
        private final ComboPooledDataSource dataSource;
        private final String connectionInfo;

        public PooledMysqlEventSource(ComboPooledDataSource dataSource, String tableName, int defaultBatchSize, String name) {
            super(dataSource::getConnection, tableName, defaultBatchSize, name);
            this.connectionInfo = dataSource.getUser() == null ? dataSource.getJdbcUrl() : dataSource.getUser() + " @ " + dataSource.getJdbcUrl();
            this.dataSource = dataSource;
        }

        @Override
        public void close() {
            dataSource.close();
        }

        @Override
        protected Optional<String> databaseConnectionInfo() {
            return Optional.of(connectionInfo);
        }
    }
}

@@ -66,35 +66,8 @@
 on;%0A
-import java.util.Optional;%0A
 impo
@@ -2480,39 +2480,29 @@
     
-Component databaseConnection = 
+return singletonList(
 new 
@@ -2578,373 +2578,16 @@
 ion)
-;%0A        if (databaseConnectionInfo().isPresent()) %7B%0A            String prefix = databaseConnectionInfo().get() + %22: %22;%0A            databaseConnection = databaseConnection.mapValue(v -%3E prefix + v);%0A        %7D%0A        return singletonList(databaseConnection);%0A    %7D%0A%0A    protected Optional%3CString%3E databaseConnectionInfo() %7B%0A        return Optional.empty();%0A    %7D%0A%0A
+);%0A    %7D
 %0A%0A  
@@ -4283,53 +4283,8 @@
 rce;
-%0A        private final String connectionInfo;
 %0A%0A  
@@ -4510,407 +4510,121 @@
 his.
-connectionInfo = dataSource.getUser() == null ? dataSource.getJdbcUrl() : dataSource.getUser() + %22 @ %22 + dataSource.getJdbcUrl();%0A            this.dataSource = dataSource;%0A        %7D%0A%0A        @Override%0A        public void close() %7B%0A            dataSource.close();%0A        %7D%0A%0A        @Override%0A        protected Optional%3CString%3E databaseConnectionInfo() %7B%0A            return Optional.of(connectionInfo
+dataSource = dataSource;%0A        %7D%0A%0A        @Override%0A        public void close() %7B%0A            dataSource.close(
 );%0A 

remove duplicate database connection info from status components