package space.nstart.pcp.pcp_request_service

import org.slf4j.LoggerFactory
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.context.ApplicationListener
import org.springframework.core.env.ConfigurableEnvironment
import org.springframework.core.env.EnumerablePropertySource
import org.springframework.core.env.PropertySource
import org.springframework.kafka.annotation.EnableKafka
import org.springframework.scheduling.annotation.EnableAsync

@SpringBootApplication
@EnableAsync
@EnableKafka
class PcpBallisticsServiceApplication

fun main(args: Array<String>) {
	runApplication<PcpBallisticsServiceApplication>(*args) {
		addListeners(BallisticsStartupConfigurationLogger())
	}
}

private class BallisticsStartupConfigurationLogger : ApplicationListener<ApplicationEnvironmentPreparedEvent> {
	private val logger = LoggerFactory.getLogger(BallisticsStartupConfigurationLogger::class.java)

	override fun onApplicationEvent(event: ApplicationEnvironmentPreparedEvent) {
		val environment = event.environment
		val activeProfiles = environment.activeProfiles.takeIf { it.isNotEmpty() } ?: environment.defaultProfiles

		logger.info(
			"Ballistics startup config client: activeProfiles={}, configServerUri={}, configProfile={}, configLabel={}",
			activeProfiles.joinToString(","),
			maskIfSensitive("spring.cloud.config.uri", environment.getProperty("spring.cloud.config.uri")),
			environment.getProperty("spring.cloud.config.profile"),
			environment.getProperty("spring.cloud.config.label"),
		)

		val configSources = environment.configServerPropertySources()
		if (configSources.isEmpty()) {
			logger.warn("Ballistics received no config server property sources")
			return
		}

		configSources.forEach { source ->
			logger.info("Ballistics received config source '{}': {}", source.name, source.printableProperties(environment))
		}
	}

	private fun ConfigurableEnvironment.configServerPropertySources(): List<PropertySource<*>> =
		propertySources
			.asSequence()
			.filter { it.name.startsWith("configserver:") || it.name == "configClient" }
			.toList()

	private fun PropertySource<*>.printableProperties(environment: ConfigurableEnvironment): String {
		if (this !is EnumerablePropertySource<*>) {
			return "<non-enumerable>"
		}

		return propertyNames
			.sorted()
			.joinToString(", ") { propertyName ->
				formatProperty(propertyName, getProperty(propertyName), environment.getProperty(propertyName))
			}
	}

	private fun formatProperty(propertyName: String, rawValue: Any?, resolvedValue: String?): String {
		val printableResolvedValue = maskIfSensitive(propertyName, resolvedValue)
		val printableRawValue = maskIfSensitive(propertyName, rawValue)

		if (printableRawValue == printableResolvedValue) {
			return "$propertyName=$printableResolvedValue"
		}

		return "$propertyName=$printableResolvedValue (raw=$printableRawValue)"
	}

	private fun maskIfSensitive(propertyName: String, value: Any?): String {
		if (value == null) {
			return "<null>"
		}

		val normalizedName = propertyName.lowercase()
		val sensitiveMarkers = listOf("password", "passwd", "secret", "token", "credential", "private-key", "api-key", "apikey")
		return if (sensitiveMarkers.any { normalizedName.contains(it) }) {
			"******"
		} else {
			value.toString()
		}
	}
}
