// el-config — Layered, typed configuration for el-ui.
//
// Each layer is a key->value map. `Config::get(key)` walks layers from highest
// to lowest priority and returns the first hit. Values are parsed into the
// caller-requested type via `parse_<type>`.

// ── Environment ──────────────────────────────────────────────────────────────

let ENV_DEVELOPMENT: String = "development"
let ENV_STAGING:     String = "staging"
let ENV_PRODUCTION:  String = "production"
let ENV_TEST:        String = "test"

fn env_current() -> String {
    let raw: String = env("EL_ENV")
    if str_eq(raw, "") { return ENV_DEVELOPMENT }
    raw
}

fn env_is_production(name: String) -> Bool {
    str_eq(name, "production")
}

// ── Errors ───────────────────────────────────────────────────────────────────

let CFG_ERR_MISSING:    String = "config.missing"
let CFG_ERR_PARSE:      String = "config.parse_error"
let CFG_ERR_TYPE:       String = "config.type_error"

// ── Sources ──────────────────────────────────────────────────────────────────

let SRC_ENV:      String = "env"
let SRC_DOTENV:   String = "dotenv"
let SRC_MANIFEST: String = "manifest"
let SRC_DEFAULTS: String = "defaults"
let SRC_MAP:      String = "map"

type ConfigSource {
    kind:        String
    priority:    Int        // higher = wins
    map_json:    String     // JSON: key -> string value
}

fn source_env_vars(prefix: String) -> ConfigSource {
    // Snapshot env at construction (the runtime exposes env_keys()).
    let map: String = "{}"
    let keys: String = env_keys_with_prefix(prefix)
    let n: Int = json_array_len(keys)
    let i: Int = 0
    while i < n {
        let raw_key: String = json_array_get(keys, i)
        let value:   String = env(raw_key)
        let logical_key: String = str_to_lower(str_replace(str_slice(raw_key, str_len(prefix), str_len(raw_key)), "_", "."))
        let map = json_set(map, logical_key, "\"" + value + "\"")
        let i = i + 1
    }
    { "kind": "env", "priority": 100, "map_json": map }
}

fn source_dotenv(path: String) -> ConfigSource {
    let raw: String = ""
    if fs_exists(path) { let raw = fs_read(path) }
    let map: String = dotenv_parse(raw)
    { "kind": "dotenv", "priority": 80, "map_json": map }
}

fn source_manifest(manifest_path: String, current_env: String) -> ConfigSource {
    let raw: String = fs_read(manifest_path)
    let base:    String = manifest_section(raw, "config")
    let env_key: String = "env." + current_env
    let env_overrides: String = manifest_section(raw, env_key)
    let map: String = json_merge(base, env_overrides)
    { "kind": "manifest", "priority": 60, "map_json": map }
}

fn source_defaults(map_json: String) -> ConfigSource {
    { "kind": "defaults", "priority": 0, "map_json": map_json }
}

// ── Config ───────────────────────────────────────────────────────────────────

type Config {
    environment:  String   // development | staging | production | test
    sources_json: String   // JSON array of ConfigSource
}

fn config_new(env_name: String) -> Config {
    { "environment": env_name, "sources_json": "[]" }
}

fn config_add_source(c: Config, src: ConfigSource) -> Config {
    let updated: String = json_array_push_sorted(c.sources_json, json_encode(src), "priority", true)
    { "environment": c.environment, "sources_json": updated }
}

fn config_set_defaults(c: Config, defaults_map: String) -> Config {
    config_add_source(c, source_defaults(defaults_map))
}

fn config_get_string(c: Config, key: String) -> String {
    let n: Int = json_array_len(c.sources_json)
    let i: Int = 0
    while i < n {
        let src_json: String = json_array_get(c.sources_json, i)
        let map:      String = json_get(src_json, "map_json")
        let v:        String = json_get(map, key)
        if !str_eq(v, "") { return v }
        let i = i + 1
    }
    ""
}

fn config_get_int(c: Config, key: String) -> Int {
    let raw: String = config_get_string(c, key)
    if str_eq(raw, "") { return 0 }
    str_to_int(raw)
}

fn config_get_bool(c: Config, key: String) -> Bool {
    let raw: String = str_to_lower(config_get_string(c, key))
    if str_eq(raw, "true") { return true }
    if str_eq(raw, "1")    { return true }
    if str_eq(raw, "yes")  { return true }
    false
}

// Strict variants — non-empty required.
fn config_require_string(c: Config, key: String) -> String {
    let v: String = config_get_string(c, key)
    if str_eq(v, "") { panic(CFG_ERR_MISSING + ":" + key) }
    v
}

// ── load_from_toml — convenience for TOML config files ──────────────────────

fn config_load_from_toml(path: String, env_name: String) -> Config {
    let cfg: Config = config_new(env_name)
    let raw: String = fs_read(path)
    let map: String = toml_to_json_flat(raw)   // dotted keys
    let cfg = config_add_source(cfg, source_defaults(map))
    cfg
}

// ── .env parser (minimal) ───────────────────────────────────────────────────

fn dotenv_parse(raw: String) -> String {
    let map: String = "{}"
    let lines: String = str_split(raw, "\n")
    let n: Int = json_array_len(lines)
    let i: Int = 0
    while i < n {
        let line: String = str_trim(json_array_get(lines, i))
        if str_eq(line, "") {
            let i = i + 1
        }
        if !str_eq(line, "") {
            if !str_starts_with(line, "#") {
                let eq: Int = str_index_of(line, "=")
                if eq > 0 {
                    let k: String = str_trim(str_slice(line, 0, eq))
                    let v: String = str_trim(str_slice(line, eq + 1, str_len(line)))
                    let v = str_strip_quotes(v)
                    let map = json_set(map, str_to_lower(str_replace(k, "_", ".")), "\"" + v + "\"")
                }
                let i = i + 1
            }
        }
    }
    map
}

// ── Entry — smoke test ──────────────────────────────────────────────────────

let cfg: Config = config_new(env_current())
let defaults: String = "{\"app.name\":\"MyApp\",\"server.port\":\"8080\"}"
let cfg = config_set_defaults(cfg, defaults)
println("[el-config] env=" + cfg.environment + " app=" + config_get_string(cfg, "app.name"))
