-- Load libraries
local exit = require "sm.utils.exit"
local global_config = require "sm.config"
local redis = require "sm.utils.redis"
local config_fetcher = require "sm.utils.config_fetcher"
local cache = require "sm.service.cache"
local random = require "sm.utils.random"
local error = require "sm.utils.error"
local cjson = require "cjson"
local string_utils = require "sm.utils.string_utils"

-- Reference native functions
local os_getenv = os.getenv
local ngx_unescape_uri = ngx.unescape_uri
local VERSION = _VERSION
local NODE_TYPE = os_getenv("NODE_TYPE")

local _M = {}

-- Random seed
random.seed()

-- 连接Redis
local function connect_redis()
    local remote_addr = ngx.var.remote_addr
    local request_id = ngx.var.request_id

    local ok, err = redis.connect(global_config["redis"])
    if not ok then
        return exit.error(remote_addr, request_id)
    end
    return true
end

-- 获取hostname和zone_id
local function get_hostname_and_zone(host)
    local remote_addr = ngx.var.remote_addr
    local request_id = ngx.var.request_id
    local hostname

    -- 先从ctx中获取hostname和zone_id，如果存在则直接使用，否则从redis中获取
    if ngx.ctx.hostname and ngx.ctx.zone_id then
        hostname = ngx.ctx.hostname
    else
        local err
        hostname, _, err = config_fetcher.hostname(redis, host)
        if not hostname then
            -- 判断是否是根域名
            if string_utils.is_root_domain(host) then
                ngx.log(ngx.ERR, "hostname not found: " .. host)
                return nil, exit.config(remote_addr, request_id)
            else
                -- 从域名中提取根域名
                local root_domain = string_utils.get_root_domain(host)
                hostname, _, err = config_fetcher.hostname(redis, "*." .. root_domain)
                if not hostname then
                    ngx.log(ngx.ERR, "hostname not found: " .. host)
                    return nil, exit.config(remote_addr, request_id)
                end
            end
        end
    end

    return hostname, nil
end

-- 设置nginx变量
local function set_nginx_vars(hostname)
    ngx.log(ngx.INFO, "hostname: " .. cjson.encode(hostname))
    ngx.var.site = cjson.encode(hostname)
    ngx.var.zone_id = hostname["key"]
    ngx.var.zstd_flag = hostname["zstd"] or "off"
    ngx.var.brotli_flag = hostname["brotli"] or "off"
    ngx.var.gzip_flag = hostname["gzip"] or "off"

    -- 存储zone_id用于全局引用
    zone_id = hostname["key"]
end

-- 处理HTTPS重定向
local function handle_https_redirect(hostname, host)
    -- 如果回源节点不校验https，则直接返回
    if NODE_TYPE == "origin" then
        return nil
    end

    if hostname["force_https"] == "2" and ngx.var.scheme == "http" then
        -- Always close Redis
        redis.close()
        -- Redirect to HTTPS
        return ngx.redirect("https://" .. host .. ngx_unescape_uri(ngx.var.request_uri))
    end
    return nil
end

-- 判断是否需要跳过防火墙验证（回源节点跳过）
local function should_skip_firewall()
    -- 如果是回源节点，跳过防火墙验证
    return NODE_TYPE == "origin"
end

-- IP防火墙检查
local function check_ip_firewall(remote_addr, request_id, zone_id)
    if should_skip_firewall() then
        ngx.log(ngx.INFO, "Origin node detected, skipping IP firewall check")
        return nil
    end

    local deny_ips, hit_level, err = config_fetcher.getDomainConf(redis, zone_id .. ":deny_ips") 
    local deny_ips_list, decode_err = pcall(cjson.decode, deny_ips)
    if deny_ips_list == nil or type(deny_ips_list) ~= "table" or #deny_ips_list == 0 then
        return nil
    end
    ngx.log(ngx.INFO, "deny_ips_list: " .. cjson.encode(deny_ips_list))
    if deny_ips_list ~= nil and type(deny_ips_list) == "table" and #deny_ips_list > 0 then
        local iputils = require "resty.iputils"
        local cidrs = iputils.parse_cidrs(deny_ips_list)
        if iputils.ip_in_cidrs(ngx.var.remote_addr, cidrs) then
            return exit.rule_block(remote_addr, request_id)
        end
    end
    return nil
end

-- ASN防火墙检查
local function check_asn_firewall(remote_addr, request_id, zone_id)
    if should_skip_firewall() then
        ngx.log(ngx.INFO, "Origin node detected, skipping ASN firewall check")
        return nil
    end

    local request_asn = ngx.var.geoip2_data_asn_number
    if request_asn ~= nil then
        -- 获取域名ASN黑名单 [12345, 67890]
        local deny_ips, hit_level, err = config_fetcher.getDomainConf(redis, zone_id .. ":deny_asn_list")
        if deny_ips ~= nil and #deny_ips > 0 then
            local deny_ips_list = cjson.decode(deny_ips)
            if deny_ips_list[request_asn] then
                return exit.rule_block(remote_addr, request_id)
            end
        end
    end
    return nil
end

-- Country防火墙检查
local function check_country_firewall(remote_addr, request_id, zone_id)
    if should_skip_firewall() then
        ngx.log(ngx.INFO, "Origin node detected, skipping Country firewall check")
        return nil
    end

    local request_country = ngx.var.geoip2_data_country_code
    if request_country ~= nil then
        -- 获取域名区域黑名单 ["HK", "CN"]
        local deny_ips, hit_level, err = config_fetcher.getDomainConf(redis, zone_id .. ":deny_country_list")
        if deny_ips ~= nil and #deny_ips > 0 then
            local deny_ips_list = cjson.decode(deny_ips)
            if deny_ips_list[request_country] then
                return exit.rule_block(remote_addr, request_id)
            end
        end
    end
    return nil
end

-- 执行所有防火墙检查
local function check_firewall_rules(remote_addr, request_id, zone_id)
    -- IP防火墙规则
    local result = check_ip_firewall(remote_addr, request_id, zone_id)
    if result then
        return result
    end

    -- ASN防火墙规则
    result = check_asn_firewall(remote_addr, request_id, zone_id)
    if result then
        return result
    end

    -- Country防火墙规则
    result = check_country_firewall(remote_addr, request_id, zone_id)
    if result then
        return result
    end

    return nil
end

-- 完成请求处理
local function finalize_request(hostname)
    ngx.var.site = cjson.encode(hostname)

    _M.ROUTE(hostname)

    -- Always close Redis
    redis.close()
end

function _M.rewrite()
    local host = ngx.var.host
    local request_id = ngx.var.request_id
    local remote_addr = ngx.var.remote_addr
    ngx.log(ngx.INFO, "host: " .. host .. ", node_type: " .. (NODE_TYPE or "undefined"))

    -- 生产环境禁用
    -- if not ngx.var.remote_addr == "206.238.224.233" then
    --     return exit.rule_block(remote_addr, request_id)
    -- end

    -- 连接Redis
    local ok = connect_redis()
    if not ok then
        return
    end

    -- 获取hostname和zone_id
    local hostname, err = get_hostname_and_zone(host)
    if not hostname then
        return
    end
    ngx.var.zone_id = hostname["key"]


    -- 设置nginx变量
    set_nginx_vars(hostname)

    -- 处理HTTPS重定向
    local redirect_result = handle_https_redirect(hostname, host)
    if redirect_result then
        return redirect_result
    end

    -- 检查防火墙规则（回源节点跳过）
    local firewall_result = check_firewall_rules(remote_addr, request_id, hostname["key"])
    if firewall_result then
        return firewall_result
    end

    -- 完成请求处理
    finalize_request(hostname)
end

function _M.ROUTE(hostname)
    -- 处理缓存
    local is_cached, cache_config = cache.handle(hostname)
    ngx.log(ngx.INFO, "is_cached: " .. cjson.encode(is_cached) .. " cache_config: " .. cjson.encode(cache_config))
    if is_cached then
        ngx.var.cache_config = cjson.encode(cache_config)
        ngx.req.set_uri("/@proxy" .. ngx.var.uri, true)
        -- Always close Redis
        redis.close()
    end
end

return _M
