-- Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
-- Copyright (c) 2023 bukale bukale2022@163.com

local cjson = require "cjson"
local config = require "config"
local user = require "user"
local rule_utils = require "lib.rule_utils"
local stringutf8 = require "stringutf8"

local get_site_config_file = config.get_site_config_file
local get_site_module_rule_file = config.get_site_module_rule_file
local update_site_config_file = config.update_site_config_file

local tonumber = tonumber
local cjson_decode = cjson.decode
local cjson_encode = cjson.encode
local trim = stringutf8.trim

local _M = {}

local MODULE_ID = 'acl'

function _M.do_request()
    local response = {code = 200, data = {}, msg = ""}
    local uri = ngx.var.uri
    local reload = false

    if user.check_auth_token() == false then
        response.code = 401
        response.msg = 'User not logged in'
        ngx.status = 401
        ngx.say(cjson_encode(response))
        ngx.exit(401)
        return
    end

    if uri == "/acl/config/get" then
        -- 查询配置信息
        local args, err = ngx.req.get_uri_args()
        if args then
            local site_id = tostring(args['siteId'])
            local _, content = get_site_config_file(site_id)
            if content then
                local config_table = cjson_decode(content)
                local acl = config_table.acl
                response.data = cjson_encode(acl)
            else
                response.code = 500
                response.msg = 'no config file found'
            end
        else
            response.code = 500
            response.msg = err
        end
    elseif uri == "/acl/config/state/update" then
        -- 修改配置
        ngx.req.read_body()
        local args, err = ngx.req.get_post_args()
        if args then
           local site_id = tostring(args['siteId'])
           local state = args.state
           local _, content = get_site_config_file(site_id)

           if state and content then
               local config_table = cjson_decode(content)
               if config_table then
                   config_table.acl.state = state
                   local new_config_json = cjson_encode(config_table)
                   update_site_config_file(site_id, new_config_json)
                   reload = true
               end
           else
               response.code = 500
               response.msg = 'param error'
           end
        else
            response.code = 500
            response.msg = err
        end
    elseif uri == "/acl/rule/list" then
        local args, err = ngx.req.get_uri_args()

        if args then
            local site_id = tostring(args['siteId'])

            if site_id then
                local file_path = get_site_module_rule_file(site_id, MODULE_ID)
                response = rule_utils.list_rules(file_path)
            else
                response.code = 500
                response.msg = 'param error'
            end
        else
            response.code = 500
            response.msg = err
        end
    elseif uri == "/acl/rule/save" then
        -- 修改或新增acl规则
        ngx.req.read_body()
        local args, err = ngx.req.get_post_args()

        if args then
            local site_id = tostring(args['siteId'])
            if site_id then
                local rule_new = rule_utils.get_rule_from_request()
                if rule_new then
                    rule_new.id = tonumber(rule_new.id)
                    rule_new.ipBlockExpireInSeconds = tonumber(rule_new.ipBlockExpireInSeconds)
                    rule_new.autoIpBlock = rule_new.autoIpBlock or 'off'
                    rule_new.attackType = 'acl'
                    rule_new.severityLevel = 'medium'

                    -- 将匹配逻辑操作符转换为对应正则表达式
                    local conditions = rule_new.conditions
                    if conditions then
                        for _, c in pairs(conditions) do
                            local operator = c.operator
                            local content = trim(c.content)
                            local pattern = ''
                            if operator == 'prefix' then
                                pattern = '^' .. content
                            elseif operator == 'suffix' then
                                pattern = content .. '$'
                            elseif operator == 'contains' then
                                pattern = content
                            elseif operator == 'not' then
                                pattern = '^(?:(?!' .. content .. ').)*$'
                            elseif operator == 'notexist' then
                                pattern = ''
                            elseif operator == 'equal' then
                                pattern = '^' .. content .. '$'
                            elseif operator == 'regex' then
                                pattern = content
                            elseif operator == 'in' then
                                pattern = ''
                            elseif operator == 'notin' then
                                pattern = ''
                            end
                            if operator ~= 'in' and operator ~= 'notin' then
                                c.ipGroupId = nil
                            else
                                c.ipGroupId = tonumber(c.ipGroupId)
                            end
                            c.pattern = pattern
                        end
                    end

                    response = rule_utils.save_or_update_site_rule(site_id, MODULE_ID, rule_new)
                    reload = true
                else
                    response.code = 500
                    response.msg = 'param error'
                end
            else
                response.code = 500
                response.msg = 'param error'
            end
        else
            response.code = 500
            response.msg = err
        end
    elseif uri == "/acl/rule/state/update" then
        -- 修改acl规则状态
        ngx.req.read_body()
        local args, err = ngx.req.get_post_args()

        if args then
            local site_id = tostring(args['siteId'])
            local rule_id = tonumber(args['ruleId'])
            local state = tostring(args['state'])

            response = rule_utils.update_site_rule_state(site_id, MODULE_ID, rule_id, state)
            if response and response.code == 200 then
                reload = true
            end
        else
            response.code = 500
            response.msg = err
        end
    elseif uri == "/acl/rule/remove" then
        -- 删除acl规则
        ngx.req.read_body()
        local args, err = ngx.req.get_post_args()

        if args then
            local site_id = tostring(args['siteId'])
            local rule_id = tonumber(args['ruleId'])

            response = rule_utils.delete_site_rule(site_id, MODULE_ID, rule_id)
            if response and response.code == 200 then
                reload = true
            end
        else
            response.code = 500
            response.msg = err
        end
        reload = true
    end

    ngx.say(cjson_encode(response))

    -- 如果没有错误且需要重载配置文件则重载配置文件
    if (response.code == 200 or response.code == 0) and reload == true then
        config.reload_config_file()
    end
end

_M.do_request()

return _M
