*! _diddesign_expand_covariates.ado - Expand covariates with factor support
*!
*! Internal helper used by diddesign, diddesign_check, and _diddesign_sa.
*! Factor-variable terms are expanded via fvrevar and base/constant columns
*! are excluded so that the downstream design matrix does not contain
*! spurious zero or constant columns.

version 16.0

program define _diddesign_expand_covariates, rclass
    version 16.0

    syntax , COVARS(string asis) TOUSE(name)

    local covars_str = trim("`covars'")
    if "`covars_str'" == "" {
        return local varlist ""
        return local generated_vars ""
        return local encoded_sources ""
        return scalar n_factor_expanded = 0
        return scalar n_constant_dropped = 0
        exit
    }

    local prepared_covars ""
    local has_fvops "`r(fvops)'"

    local expanded_vars ""
    local generated_vars ""
    local n_fv_expanded = 0
    local n_constant_dropped = 0
    local generated_counter = 0
    local encoded_sources ""
    local encoded_targets ""

    foreach covar_term of local covars_str {
        local token_stream = subinstr("`covar_term'", "##", " __diddesign_dhash__ ", .)
        local token_stream = subinstr("`token_stream'", "#", " # ", .)
        local token_stream = subinstr("`token_stream'", "__diddesign_dhash__", " # # ", .)
        local rebuilt_term ""

        foreach token of local token_stream {
            if "`token'" == "#" {
                local rebuilt_term "`rebuilt_term'#"
            }
            else {
                local token_prefix ""
                local token_var "`token'"
                local dot_pos = strpos("`token'", ".")
                local token_use "`token'"

                if `dot_pos' > 0 {
                    local token_prefix = substr("`token'", 1, `dot_pos')
                    local token_var = substr("`token'", `dot_pos' + 1, .)
                }

                capture confirm string variable `token_var'
                if _rc == 0 {
                    local token_prefix_lower = lower("`token_prefix'")
                    local is_factor_token = regexm("`token_prefix_lower'", "^i(bn?|b?[0-9]*)?\.$")

                    if !`is_factor_token' {
                        display as error "E017: String covariate `token_var' must use factor-variable notation"
                        exit 109
                    }

                    local source_pos : list posof "`token_var'" in encoded_sources
                    if `source_pos' > 0 {
                        local token_target : word `source_pos' of `encoded_targets'
                    }
                    else {
                        local ++generated_counter
                        local token_target "__diddesign_sfv_`generated_counter'"
                        capture confirm new variable `token_target'
                        while _rc != 0 {
                            local ++generated_counter
                            local token_target "__diddesign_sfv_`generated_counter'"
                            capture confirm new variable `token_target'
                        }
                        quietly encode `token_var' if `touse', gen(`token_target')
                        local generated_vars "`generated_vars' `token_target'"
                        local encoded_sources "`encoded_sources' `token_var'"
                        local encoded_targets "`encoded_targets' `token_target'"
                    }

                    local token_use "`token_prefix'`token_target'"
                }

                local rebuilt_term "`rebuilt_term'`token_use'"
            }
        }

        local prepared_covars "`prepared_covars' `rebuilt_term'"
    }

    local prepared_covars : list retokenize prepared_covars

    fvexpand `prepared_covars' if `touse'
    local has_fvops "`r(fvops)'"

    if "`has_fvops'" == "true" {
        foreach covar_term of local prepared_covars {
            local is_factor = 0
            if regexm("`covar_term'", "^i\.") | regexm("`covar_term'", "^i\(") {
                local is_factor = 1
            }
            if regexm("`covar_term'", "^ibn\.") | regexm("`covar_term'", "^ib[0-9]+") {
                local is_factor = 1
            }
            if regexm("`covar_term'", "^c\.") {
                local is_factor = 0
            }

            if `is_factor' {
                fvrevar `covar_term' if `touse'
                local fv_vars "`r(varlist)'"
                local n_fv : word count `fv_vars'
                local kept_vars ""
                local dropped_any_constant = 0

                foreach v of local fv_vars {
                    quietly summarize `v' if `touse', meanonly
                    if r(N) == 0 {
                        continue
                    }
                    if r(min) == r(max) {
                        local dropped_any_constant = 1
                        local n_constant_dropped = `n_constant_dropped' + 1
                    }
                    else {
                        local kept_vars "`kept_vars' `v'"
                    }
                }

                if "`kept_vars'" != "" {
                    local n_kept : word count `kept_vars'
                    local persistent_vars ""
                    foreach v of local kept_vars {
                        local use_var "`v'"
                        if substr("`v'", 1, 2) == "__" {
                            local ++generated_counter
                            local use_var "__diddesign_fv_`generated_counter'"
                            capture confirm new variable `use_var'
                            while _rc != 0 {
                                local ++generated_counter
                                local use_var "__diddesign_fv_`generated_counter'"
                                capture confirm new variable `use_var'
                            }
                            quietly gen double `use_var' = `v'
                            local generated_vars "`generated_vars' `use_var'"
                        }
                        local persistent_vars "`persistent_vars' `use_var'"
                    }
                    local expanded_vars "`expanded_vars' `persistent_vars'"
                    local n_fv_expanded = `n_fv_expanded' + `n_kept'
                }
            }
            else {
                capture fvrevar `covar_term' if `touse'
                if _rc == 0 {
                    local resolved_vars "`r(varlist)'"
                    local persistent_vars ""
                    foreach v of local resolved_vars {
                        local use_var "`v'"
                        if substr("`v'", 1, 2) == "__" {
                            local ++generated_counter
                            local use_var "__diddesign_fv_`generated_counter'"
                            capture confirm new variable `use_var'
                            while _rc != 0 {
                                local ++generated_counter
                                local use_var "__diddesign_fv_`generated_counter'"
                                capture confirm new variable `use_var'
                            }
                            quietly gen double `use_var' = `v'
                            local generated_vars "`generated_vars' `use_var'"
                        }
                        local persistent_vars "`persistent_vars' `use_var'"
                    }
                    local expanded_vars "`expanded_vars' `persistent_vars'"
                }
                else {
                    local expanded_vars "`expanded_vars' `covar_term'"
                }
            }
        }
    }
    else {
        foreach covar_term of local prepared_covars {
            capture fvrevar `covar_term' if `touse'
            if _rc == 0 {
                local resolved_vars "`r(varlist)'"
                local persistent_vars ""
                foreach v of local resolved_vars {
                    local use_var "`v'"
                    if substr("`v'", 1, 2) == "__" {
                        local ++generated_counter
                        local use_var "__diddesign_fv_`generated_counter'"
                        capture confirm new variable `use_var'
                        while _rc != 0 {
                            local ++generated_counter
                            local use_var "__diddesign_fv_`generated_counter'"
                            capture confirm new variable `use_var'
                        }
                        quietly gen double `use_var' = `v'
                        local generated_vars "`generated_vars' `use_var'"
                    }
                    local persistent_vars "`persistent_vars' `use_var'"
                }
                local expanded_vars "`expanded_vars' `persistent_vars'"
            }
            else {
                local expanded_vars "`expanded_vars' `covar_term'"
            }
        }
    }

    local expanded_vars : list retokenize expanded_vars
    local generated_vars : list retokenize generated_vars
    local encoded_sources : list retokenize encoded_sources

    return local varlist "`expanded_vars'"
    return local generated_vars "`generated_vars'"
    return local encoded_sources "`encoded_sources'"
    return scalar n_factor_expanded = `n_fv_expanded'
    return scalar n_constant_dropped = `n_constant_dropped'
end