# ============================================================
# Nginx lua_shared_dict 共享字典配置示例
# ============================================================
#
# 功能说明:
# - 跨请求共享数据
# - 实现缓存、计数器、限流
# - 进程间通信
#
# Lolly 对应配置:
# lolly 内置 Lua 沙箱，支持共享字典功能
# 相关文档: docs/lua-nginx-module/05-shdict.md
# ============================================================

http {
    # 定义共享字典
    # lua_shared_dict name size;
    # size: 内存大小，支持 k, m 单位
    lua_shared_dict cache 10m;        # 缓存存储
    lua_shared_dict rate_limit 5m;    # 限流计数
    lua_shared_dict sessions 10m;     # 会话存储
    lua_shared_dict config 1m;        # 配置存储

    server {
        listen 80;
        server_name shdict.example.com;

        # 缓存示例
        location /api/cached {
            content_by_lua_block {
                local cjson = require "cjson.safe"
                local cache = ngx.shared.cache

                local key = ngx.var.arg_key or "default"

                -- 尝试从缓存获取
                local value, flags = cache:get(key)

                if value then
                    ngx.header["X-Cache"] = "HIT"
                    ngx.say(value)
                    return
                end

                -- 生成数据（模拟）
                local data = {
                    key = key,
                    value = "generated_value_" .. key,
                    timestamp = ngx.now()
                }
                local json = cjson.encode(data)

                -- 存入缓存，60 秒过期
                local success, err, forcible = cache:set(key, json, 60)
                if not success then
                    ngx.log(ngx.WARN, "failed to set cache: ", err)
                end

                -- 检查是否 LRU 强制淘汰
                if forcible then
                    ngx.log(ngx.WARN, "cache LRU eviction occurred")
                end

                ngx.header["X-Cache"] = "MISS"
                ngx.say(json)
            }
        }

        # 限流示例
        location /api/limited {
            access_by_lua_block {
                local limit = ngx.shared.rate_limit
                local ip = ngx.var.remote_addr
                local key = "limit:" .. ip

                -- 获取当前计数
                local count, err = limit:incr(key, 1, 0, 60)  # 60 秒窗口

                if not count then
                    ngx.log(ngx.ERR, "failed to incr: ", err)
                    count = 0
                end

                -- 设置过期时间（仅首次）
                if count == 1 then
                    limit:expire(key, 60)
                end

                -- 检查限制
                local max_requests = 100
                if count > max_requests then
                    ngx.status = 429
                    ngx.header["X-RateLimit-Limit"] = max_requests
                    ngx.header["X-RateLimit-Remaining"] = 0
                    ngx.header["X-RateLimit-Reset"] = 60
                    ngx.say('{"error": "Rate limit exceeded"}')
                    ngx.exit(429)
                end

                -- 设置响应头
                ngx.header["X-RateLimit-Limit"] = max_requests
                ngx.header["X-RateLimit-Remaining"] = math.max(0, max_requests - count)
            }

            content_by_lua_block {
                ngx.say('{"status": "ok"}')
            }
        }

        # 会话存储示例
        location /api/session {
            content_by_lua_block {
                local cjson = require "cjson.safe"
                local sessions = ngx.shared.sessions

                local session_id = ngx.var.cookie_session_id

                if ngx.req.get_method() == "GET" then
                    -- 获取会话
                    if not session_id then
                        ngx.status = 401
                        ngx.say('{"error": "No session"}')
                        ngx.exit(401)
                    end

                    local session_data = sessions:get("session:" .. session_id)
                    if not session_data then
                        ngx.status = 401
                        ngx.say('{"error": "Session expired"}')
                        ngx.exit(401)
                    end

                    ngx.say(session_data)

                elseif ngx.req.get_method() == "POST" then
                    -- 创建会话
                    ngx.req.read_body()
                    local data = cjson.decode(ngx.req.get_body_data())

                    -- 生成 session ID
                    local session_id = ngx.md5(ngx.now() .. math.random())

                    -- 存储会话，30 分钟过期
                    sessions:set("session:" .. session_id, cjson.encode(data), 1800)

                    -- 设置 Cookie
                    ngx.header["Set-Cookie"] = "session_id=" .. session_id .. "; Path=/; HttpOnly"

                    ngx.say(cjson.encode({session_id = session_id}))
                end
            }
        }

        # 配置管理示例
        location /admin/config {
            content_by_lua_block {
                local cjson = require "cjson.safe"
                local config = ngx.shared.config

                local method = ngx.req.get_method()

                if method == "GET" then
                    local keys = config:get_keys()
                    local result = {}
                    for _, key in ipairs(keys) do
                        result[key] = config:get(key)
                    end
                    ngx.say(cjson.encode(result))

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

                    for k, v in pairs(data) do
                        config:set(k, v)
                    end

                    ngx.say(cjson.encode({success = true}))
                end
            }
        }

        # 统计信息
        location /admin/stats {
            content_by_lua_block {
                local cjson = require "cjson.safe"

                local function get_stats(dict_name)
                    local dict = ngx.shared[dict_name]
                    return {
                        capacity = dict:capacity(),
                        free_space = dict:free_space(),
                        keys = #dict:get_keys()
                    }
                end

                local stats = {
                    cache = get_stats("cache"),
                    rate_limit = get_stats("rate_limit"),
                    sessions = get_stats("sessions"),
                    config = get_stats("config")
                }

                ngx.header["Content-Type"] = "application/json"
                ngx.say(cjson.encode(stats))
            }
        }
    }
}

# lua_shared_dict API 说明:
#
# 1. 基础操作:
#    - get(key): 获取值
#    - set(key, value, exptime?, flags?): 设置值
#    - add(key, value, exptime?, flags?): 仅当 key 不存在时设置
#    - replace(key, value, exptime?, flags?): 仅当 key 存在时替换
#    - delete(key): 删除
#
# 2. 计数操作:
#    - incr(key, value?, init?, init_ttl?): 增加
#    - decr(key, value?): 减少
#    - flush_all(): 清空所有
#    - flush_expired(max_count?): 清理过期
#
# 3. 过期管理:
#    - expire(key, exptime): 设置过期时间
#    - ttl(key): 获取剩余过期时间
#
# 4. 统计:
#    - capacity(): 总容量（字节）
#    - free_space(): 剩余空间（字节）
#    - get_keys(max_count?): 获取所有 key
#
# 5. 特点:
#    - 所有 worker 共享
#    - LRU 淘汰策略
#    - 原子操作
#    - 支持过期时间