package su.xserver.iikocon.config;

import io.vertx.core.json.JsonObject;
import java.util.Map;

public class AppConfig {

  public ServerConfig server;
  public DatabaseConfig database;
  public RedisConfig redis;

  public static AppConfig from(JsonObject json) {
    JsonObject resolved = json.copy();
    applyEnvOverrides(resolved);
    return resolved.mapTo(AppConfig.class);
  }

  private static void applyEnvOverrides(JsonObject config) {
    Map<String, String> env = System.getenv();

    env.forEach((key, value) -> {
      String path = envKeyToPath(key);

      if (path != null && exists(config, path)) {
        setValue(config, path, value);
      }
    });
  }

  /**
   * SERVER__MAX_POOL_SIZE -> server.maxPoolSize
   */
  private static String envKeyToPath(String envKey) {
    if (!envKey.contains("__")) {
      return null;
    }

    String[] levels = envKey.toLowerCase().split("__");

    for (int i = 0; i < levels.length; i++) {
      levels[i] = toCamelCase(levels[i]);
    }

    return String.join(".", levels);
  }

  private static String toCamelCase(String value) {
    String[] parts = value.split("_");
    StringBuilder sb = new StringBuilder(parts[0]);

    for (int i = 1; i < parts.length; i++) {
      sb.append(Character.toUpperCase(parts[i].charAt(0)))
        .append(parts[i].substring(1));
    }

    return sb.toString();
  }

  private static boolean exists(JsonObject json, String path) {
    String[] parts = path.split("\\.");
    JsonObject current = json;

    for (int i = 0; i < parts.length - 1; i++) {
      if (!current.containsKey(parts[i])) {
        return false;
      }
      current = current.getJsonObject(parts[i]);
    }
    return current.containsKey(parts[parts.length - 1]);
  }

  private static void setValue(JsonObject json, String path, String value) {
    String[] parts = path.split("\\.");
    JsonObject current = json;

    for (int i = 0; i < parts.length - 1; i++) {
      current = current.getJsonObject(parts[i]);
    }

    String key = parts[parts.length - 1];
    Object oldValue = current.getValue(key);
    current.put(key, cast(value, oldValue));
  }

  private static Object cast(String value, Object oldValue) {
    if (oldValue instanceof Integer) return Integer.parseInt(value);
    if (oldValue instanceof Long) return Long.parseLong(value);
    if (oldValue instanceof Boolean) return Boolean.parseBoolean(value);
    if (oldValue == null) return value;
    return value;
  }

  public JsonObject json() {
    return new JsonObject()
      .put("server", server.json().getJsonObject("server"))
      .put("database", database.json().getJsonObject("database"))
      .put("redis", redis.json().getJsonObject("redis"));
  }

  @Override
  public String toString() {
    return json().encode();
  }
}

