package io.activej.config;

import io.activej.config.converter.ConfigConverter;
import io.activej.reactor.net.ServerSocketSettings;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

import static io.activej.config.ConfigTestUtils.testBaseConfig;
import static io.activej.config.converter.ConfigConverters.ofLong;
import static io.activej.config.converter.ConfigConverters.ofServerSocketSettings;
import static junit.framework.TestCase.assertNotNull;
import static org.junit.Assert.*;

public class EffectiveConfigTest {
	@Rule
	public final TemporaryFolder temporaryFolder = new TemporaryFolder();

	private EffectiveConfig config;

	@Before
	public void setUp() {
		Map<String, Config> db = new LinkedHashMap<>();
		db.put("jdbcUrl", Config.ofValue("jdbc:mysql://localhost:3306/some_schema"));
		db.put("username", Config.ofValue("root"));
		db.put("password", Config.ofValue("root"));
		Config dbConfig = Config.ofConfigs(db);

		Map<String, Config> server = new LinkedHashMap<>();
		server.put("port", Config.ofValue("8080"));
		server.put("businessTimeout", Config.ofValue("100ms"));
		Map<String, Config> asyncClient = new LinkedHashMap<>();
		asyncClient.put("clientTimeout", Config.ofValue("1000"));
		server.put("AsyncClient", Config.ofConfigs(asyncClient));
		Config serverConfig = Config.ofConfigs(server);
		Map<String, Config> root = new LinkedHashMap<>();

		root.put("DataBase", dbConfig);
		root.put("Server", serverConfig);

		config = EffectiveConfig.wrap(Config.ofConfigs(root));
	}

	@Test
	public void testBase() {
		Map<String, Config> tier3 = new HashMap<>();
		tier3.put("a", Config.ofValue("1"));
		tier3.put("b", Config.ofValue("2"));
		Map<String, Config> tier2 = new HashMap<>();
		tier2.put("a", Config.ofConfigs(tier3));
		tier2.put("b", Config.ofValue("3"));
		Map<String, Config> tier1 = new HashMap<>();
		tier1.put("a", Config.ofConfigs(tier2));
		tier1.put("b", Config.ofValue("4"));
		Config config = Config.ofConfigs(tier1);

		testBaseConfig(config);
	}

	@Test
	public void testCompoundConfig() {
		config = EffectiveConfig.wrap(
			Config.create()
				.with("Server.socketSettings.backlog", "10")
				.with("Server.socketSettings.receiveBufferSize", "10")
				.with("Server.socketSettings.reuseAddress", "true")
		);

		ConfigConverter<ServerSocketSettings> converter = ofServerSocketSettings();

		ServerSocketSettings settings = config.get(converter, "Server.socketSettings");
		settings = config.get(converter, "Server.socketSettings", settings);

		assertEquals(10, settings.getBacklog());
		assertEquals(10, settings.getReceiveBufferSize().toInt());
		assertTrue(settings.getReuseAddress());
	}

	@Test
	public void testWorksWithDefaultNulls() {
		String expected = """
			# a.a.a =\s
			## a.a.b = value1
			a.a.c = value2
			## a.b.a = value3
			""";

		EffectiveConfig config = EffectiveConfig.wrap(
			Config.create()
				.with("a.a.b", "value1")
				.with("a.a.c", "value2")
				.with("a.b.a", "value3")
		);

		assertNull(config.get("a.a.a", null));
		assertNotNull(config.get("a.a.c", null));

		String actual = config.renderEffectiveConfig();
		assertEquals(expected, actual);
	}

	@Test
	public void testEffectiveConfig() throws IOException {
		assertEquals("jdbc:mysql://localhost:3306/some_schema", config.get("DataBase.jdbcUrl"));
		assertEquals("root", config.get("DataBase.password"));
		assertEquals("root", config.get("DataBase.password", "default"));

		assertEquals(1000L, (long) config.get(ofLong(), "Server.AsyncClient.clientTimeout"));
		Path outputPath = temporaryFolder.newFile("./effective.properties").toPath();
		config.saveEffectiveConfigTo(outputPath);
		assertTrue(Files.exists(outputPath));
	}
}
