# ============================================================
# Nginx balancer_by_lua 动态负载均衡配置示例
# ============================================================
#
# 功能说明:
# - 使用 Lua 实现动态负载均衡
# - 服务发现集成
# - 健康检查
#
# Lolly 对应配置:
# 可通过 Lua 实现自定义负载均衡逻辑
# 相关文档: docs/lua-nginx-module/08-balancer.md
# ============================================================

http {
    lua_shared_dict upstream_list 10m;
    lua_shared_dict health_status 5m;

    # 初始化上游服务器列表
    init_by_lua_block {
        local upstream_list = ngx.shared.upstream_list

        -- 初始服务器列表
        local servers = {
            {host = "backend1", port = 8080, weight = 3},
            {host = "backend2", port = 8080, weight = 2},
            {host = "backend3", port = 8080, weight = 1},
        }

        -- 存储到共享字典
        local cjson = require "cjson.safe"
        upstream_list:set("api_servers", cjson.encode(servers))
    }

    # 后台健康检查
    init_worker_by_lua_block {
        local health = require "health_checker"

        -- 每 10 秒检查一次
        local function check_health(premature)
            if premature then return end

            local upstream_list = ngx.shared.upstream_list
            local health_status = ngx.shared.health_status
            local cjson = require "cjson.safe"

            local servers_raw = upstream_list:get("api_servers")
            local servers = cjson.decode(servers_raw)

            for _, server in ipairs(servers) do
                local key = server.host .. ":" .. server.port
                -- 发送健康检查请求（使用 cosocket）
                local ok = health.check(server.host, server.port)
                health_status:set(key, ok and "healthy" or "unhealthy")
            end

            -- 递归调度下次检查
            ngx.timer.every(10, check_health)
        end

        -- 启动定时器
        ngx.timer.at(0, check_health)
    }

    # 定义上游
    upstream dynamic_backend {
        server 0.0.0.1;  # 占位符，实际由 balancer_by_lua 选择

        balancer_by_lua_block {
            local balancer = require "ngx.balancer"
            local upstream_list = ngx.shared.upstream_list
            local health_status = ngx.shared.health_status
            local cjson = require "cjson.safe"

            -- 获取服务器列表
            local servers_raw = upstream_list:get("api_servers")
            local servers = cjson.decode(servers_raw)

            -- 过滤健康服务器
            local healthy_servers = {}
            for _, server in ipairs(servers) do
                local key = server.host .. ":" .. server.port
                if health_status:get(key) == "healthy" then
                    -- 根据权重添加多次
                    for _ = 1, server.weight do
                        table.insert(healthy_servers, server)
                    end
                end
            end

            if #healthy_servers == 0 then
                ngx.log(ngx.ERR, "No healthy servers available")
                return ngx.exit(503)
            end

            -- 轮询选择（可替换为其他算法）
            local idx = ngx.var.request_id:byte(1) % #healthy_servers + 1
            local selected = healthy_servers[idx]

            -- 设置当前请求的上游
            local ok, err = balancer.set_current_peer(selected.host, selected.port)
            if not ok then
                ngx.log(ngx.ERR, "failed to set peer: ", err)
                return ngx.exit(500)
            end
        }
    }

    server {
        listen 80;
        server_name balancer-lua.example.com;

        location /api {
            proxy_pass http://dynamic_backend;
            proxy_set_header Host $host;
        }

        # 动态更新服务器列表 API
        location /admin/servers {
            content_by_lua_block {
                local cjson = require "cjson.safe"
                local upstream_list = ngx.shared.upstream_list

                local method = ngx.req.get_method()

                if method == "GET" then
                    local servers = upstream_list:get("api_servers")
                    ngx.header["Content-Type"] = "application/json"
                    ngx.say(servers)

                elseif method == "POST" then
                    ngx.req.read_body()
                    local data = cjson.decode(ngx.req.get_body_data())

                    -- 验证数据格式
                    if not data or not data.servers then
                        ngx.status = 400
                        ngx.say(cjson.encode({error = "Invalid data"}))
                        ngx.exit(400)
                    end

                    upstream_list:set("api_servers", cjson.encode(data.servers))
                    ngx.say(cjson.encode({success = true}))

                else
                    ngx.status = 405
                    ngx.say(cjson.encode({error = "Method not allowed"}))
                end
            }
        }
    }
}

# balancer_by_lua 说明:
#
# 1. 核心功能:
#    - 动态选择上游服务器
#    - 支持自定义负载均衡算法
#    - 集成服务发现
#
# 2. ngx.balancer API:
#    - set_current_peer(host, port): 设置目标服务器
#    - set_more_tries(count): 设置重试次数
#    - get_last_failure(): 获取上次失败信息
#
# 3. 使用场景:
#    - 动态服务发现（Consul、etcd）
#    - 基于权重的负载均衡
#    - 基于延迟的路由
#    - A/B 测试流量分发
#
# 4. 注意事项:
#    - 需要占位符 server 指令
#    - 健康检查需要自行实现
#    - 使用 cosocket 避免阻塞