local ss = analiz.libs.serv
local log = analiz.libs.logserv
log.init()
SetSecLevel()

list_ver_id = GetCurrentListID({pwd_plib = "PLIB"})
ss.l("list_ver_id:" .. list_ver_id)

local uniq_id = ss.b("$(service_family):$(UNIQUE_IDENTIFIER)")
rstatus = CreateRecordStatusObject({name = uniq_id, lazy = true})

local fgenerator = CreateGenerateThrow()
fgenerator:init("bot")

last_corrid = nil
last_bid = nil
last_cache = nil

internal_state = nil

seclevel = CreateSecLevel("post")

local is_restart_new_version = false

local function sec_fun(key, val) return seclevel:get("data", key, val) end

function go_pskill()
ss.l(debug.traceback())
    ss.l("go_pskill")
    ss.e("calc_param pskill_exe $(_ANALIZ_BIN)\\pskill.exe")
    ss.e("GET_SHORT_PATH pskill_exe $(pskill_exe)")
    ss.e("GET_CURRENT_PROCESS_ID txm_pid")
    ss.e("echo execute $(pskill_exe) $(txm_pid)")
    ss.e("execute $(pskill_exe) $(txm_pid)")
    ss.e("_abort 1")
end

if global_failed ~= nil then
    ss.l("failed:" .. ss.def(global_failed, "failed"))
    ss.e("sleep sec 15")
    go_pskill()
end

-- debug.start
if false then
    do
        -- destroy Engines
        for k, v in pairs(post_engines) do
            ss.l("DestroyEngine:" .. k)
            v:DestroyEngine()
        end

        ss.a("126") -- for test
    end
end

-- debug.end

local pwd = ss.CreatePwdServ()
local tick = CreateTimeServ()

local ll = analiz.libs.logs
local finn_serv = analiz.mix.finnova.serv
local dt_serv = CreateDateTime()

-- local clog =  ll.CreateCompactLog2({limit=80,limit_dt=600,separator=""})

local bot2nc = CreateBot2Nc()
local xml_io = CreateXmlIO()

local post_configs = {}
post_configs.engines = {}
post_configs.default_engine = post_engines.first
post_configs.unknownConfig = post_engines.unknownConfig

ss.l("copy engines.start")
do
    for k, v in pairs(post_engines) do
        ss.l("engine:'" .. k .. "'")
        post_configs.engines[k] = v
    end
end
ss.l("copy engines.end")

local mem = ss.CreateMemItem(10 * 1024)
local prop_loader = finn_serv.CreatePropLoader()

local time_stat = {count = 0, last = 0, sum = 0}

local function add_(m, v)
    if m == "" then return v end
    if m == nil then return v end

    return m .. "," .. ss.def(v, "")
end

function time_stat:startProcessing() self.start = tick.now() end

function time_stat:endProcessing()
    local delta = tick.to_msec(tick.now() - self.start)
    self.last = delta
    self.sum = self.sum + delta
    self.count = self.count + 1
end

function time_stat:show()
    ss.l("time.stat: count:" .. self.count .. " last:" .. self.last ..
             "msec avg:" .. self.sum / self.count)
end

local function send_(inf, enq, b1, data, corrid, prior_arg, jheader)
    b1:write(data)

    local o = {}

    --  local prior_arg=1
    local expiration_arg = 5 * 180 * 100

    o.BYTES_LOB = b1
    finn_serv.send_simple_ex(inf, enq, o, {BYTES_LOB = "blob"}, {
        correlation = corrid,
        header = jheader,
        prior = prior_arg,
        expiration = expiration_arg
    })
end

-- -------------------------init
ss.l("queue.engine init...")
-- local engine = CreateOracleEngine()
local engine = CreateQueueEngine()
engine:init()

-- local engine = oracle_engine:Create({})
-- local con = engine.con
-- local  inf = con:TypeInfoGet("SYS.AQ$_JMS_BYTES_MESSAGE")

local input_queue_name = global_oracle_setting.internal.post_queue
ss.l("create reader:" .. input_queue_name)
local reader = engine:createReader({queue = input_queue_name})

-- deq:settimeout(1)

ss.l("writer create..")
local writer = engine:createWriter({
    name = "final",
    mode = "writer2",
    queue = global_oracle_setting.finnova.out
})

ss.l("f_ailed.writer setting..")
global_failedQueue = writer
global_engine = engine
init_failed_info()

local function add_item(nc_queue_prop, name_arg, val_arg)
    nc_queue_prop[#nc_queue_prop + 1] = {name = name_arg, val = val_arg}
end

local function setting_post_header(nc_queue_prop, bid, corrid, data_ex, err)

    if err ~= nil then ret_code = "1" end
    ss.l("--setting_post_header(neo)")
    --     ss.tprint(data_ex)
    log.tprint_array(data_ex.cache,
                     {transform = sec_fun, level = log.level.DEBUG})
    ss.l("--")
    for i = 1, #data_ex.arr do
        local v = data_ex.arr[i]
        if v == nil then
            ss.l("failed.i:" .. i .. " data_ex.arr[i]=nil")
        else
            --         ss.l("i:"..i.." :"..ss.def(v.name,"NULL").." :"..ss.def(v.val,"<NULL>"))
            if v.name ~= nil then
                add_item(nc_queue_prop, v.name, ss.def(v.val, ""))
            end
        end
    end
    ss.l("-------------------------")
    ss.l("setting_post_header.end(neo)")
end

-- -------------------------------------------------------------------------
local function setting_post_header_old(header, bid, corrid, data_ex, err)
    local jmsheader = header

    jmsheader:clear()

    jmsheader:set_type("JmsBytesMessage")
    jmsheader:set_appid("postService")
    jmsheader:set_protocol(0)
    jmsheader:set_groupid('lex')
    jmsheader:set_groupseq(1)

    local ret_code = "0"

    if err ~= nil then ret_code = "1" end
    ss.l("--setting_post_header")
    --     ss.tprint(data_ex)
    log.tprint_array(data_ex.cache,
                     {transform = sec_fun, level = log.level.DEBUG})
    ss.l("--")
    for i = 1, #data_ex.arr do
        local v = data_ex.arr[i]
        if v == nil then
            ss.l("failed.i:" .. i .. " data_ex.arr[i]=nil")
        else
            --         ss.l("i:"..i.." :"..ss.def(v.name,"NULL").." :"..ss.def(v.val,"<NULL>"))
            if v.name ~= nil then
                jmsheader:set_string_property(v.name, ss.def(v.val, ""));
            end
        end
    end
    ss.l("-------------------------")
    jmsheader:show()
    ss.l("setting_post_header.end")
end

-- -------------------------------------------------------------------------

-- -------------------------------------------------------------------------
-- -------------------------------------------------------------------------
-- -------------------------------------------------------------------------

local function postProcessing(post_data)
    --   local err= bot2nc:save(post_data) -- todo add check error
    --   if err~=nil then return err end
    xml_io:send_to_db(post_data.data.bid)

end
-- -------------------------------------------------------------------------
-- -------------------------------------------------------------------------
-- -------------------------------------------------------------------------
local function ProcessingRecord(ctx, cmd)

    time_stat:startProcessing()
    --  local ret,err= post_engines.first:ProcessingRecord(cmd)
    --  local ret,err= post_engines.second:ProcessingRecord(cmd)
    local name = ss.def(cmd.cache["PROSPERO_POST_CONFIG_NAME"], "zz1aa3")

    -- check signal for restart
    if cmd.cache["PROSPERO_CHANGE_LIST_VERSION"] == "1" then
        ss.l("check version list")
        local new_list_id = GetCurrentListID({pwd_plib = "PLIB"})

        if new_list_id ~= list_ver_id then
            ss.l("detect new version list:" .. new_list_id)
            ss.l("service restart")
            is_restart_new_version = true
        end
        return nil, nil, true, nil
    end

    ss.l("go-engine:" .. name)
    local engine = post_configs.engines[name]
    if engine == nil then
        ss.l("engine==nil.use default engine")
        if post_configs.unknownConfig ~= nil then
            engine = post_configs.unknownConfig
        else
            engine = post_configs.default_engine
        end
    end
    local ret, err, post_data = engine:ProcessingRecord(ctx, cmd)

    if (post_data ~= nil) and (err == nil) then
        err = postProcessing(post_data)
    end

    time_stat:endProcessing()
    time_stat:show()

    return ret, err, nil, post_data
end

function clear_last_info()
    last_corrid = nil
    last_bid = nil
    last_cache = nil
    internal_state = nil
    last_prior = nil

end
-- -------------------------------------------------------------------------
-- -------------------------------------------------------------------------
-- -------------------------------------------------------------------------

local wait_count = 0

-- ----------main loop
function main_(ctx)

    clear_last_info()

    deadline:DeadLineLazy("wait", deadline.time.WAIT)
    local msg = ctx.reader:read();

    if msg:isnil() then
        wait_count = wait_count + 1
        xml_io:clean_old()
        rstatus:commit({lazy = true})
        return
    end

    local prior = msg:getPrior()
    ss.l("msg.prior:" .. prior)
    ss.l("msg.expiration:" .. msg:getExpiration())

    local state = msg:getState()
    ss.l("msg.state:" .. state)
    if ctx.engine:serv():IsExpired(state) then
        ss.l("msg is expired")
        wait_count = wait_count + 1
        return
    end

    if analiz:IsStop() then
        con:rollback()
        return
    end

    local corrid = msg:getCorrelation()
    local prior_msg = msg:getPrior()

    --   local obj = msg:getObj();

    local val = msg:property()
    last_cache = val.cache
    last_bid = val.cache.BID

    local cid = val.cache.CORRELATION_ID

    local profile = nil
    if cid ~= nil then
        profile = corrid
        corrid = cid
    else
        profile = "default_profile"
    end

    last_corrid = corrid

    ss.l("corrid:" .. ss.def(corrid, "<nil>") .. " profile:" .. profile)
    log.toJson(val.cache, log.level.INFO, {transform = sec_fun})

    ss.l("load body..")
    local body = msg:body()
    ss.l("load body..ok")

    if val.cache.PROSPERO_ASSERT_TEST == "1" then
        ss.l("test assert:" .. val.cache.PROSPERO_ASSERT_TEST_SERVICE)
        fgenerator:doit(val.cache.PROSPERO_ASSERT_TEST_SERVICE)
        ss.l("test assert:" .. val.cache.PROSPERO_ASSERT_TEST_SERVICE ..
                 ":ignore test")
        return
    end

    local bid = ss.def(val.cache.BID, "BID_todo_<releaseServ.stub>")
    rstatus:add(bid, corrid, "post.start", "todo")

    ss.l("--------------ProcessingRecord.Before.BID:" .. ss.def(bid, "<nul>"))
    deadline:DeadLine("processing", deadline.time.PROCESSING)
    local ret, err, is_no_send, post_data = ProcessingRecord(ctx, val)

    if post_data == nil then post_data = {bot = {}} end

    if is_no_send then
        ss.l("no send to output queue")
    else

        local out_xml = ss.def(ret.body, body)
        out_xml = ss.def(out_xml, "todo:post_finnova_message")
        ss.l("out xml:'" .. out_xml .. "'")

        local nc_queue_prop = {}
        setting_post_header(nc_queue_prop, bid, corrid, ret, err)

        local cache_i = {}

        local function add_a_(key)
            if cache_i[key] ~= nil then return end
            cache_i[key] = 1
            if val.cache[key] ~= nil then
                -- jmsheader:set_string_property(key,""..val.cache[key])
                add_item(nc_queue_prop, key, val.cache[key])
            end
        end
        add_a_("F_PAYMENT_SYSTEM")
        add_a_("F_PAYMENT_TYPE")
        add_a_("F_PROCESS_CODE")
        add_a_("PROCESS_CODE")
        add_a_("F_CS_MSG_LNR")
        add_a_("F_RECORD_TYPE")
        add_a_("CF_KD_PEP_CD")
        add_a_("CF_SANCTIONS_CD")
        add_a_("CF_INTERNAL_CD")
        add_a_("CF_CRIME_CD")

        function a_list(name)
            local root = global_oracle_setting.fields.interface
            if root == nil then return end
            local f = root[name]
            if f == nil then return end
            for i = 1, #f do add_a_(f[i]) end
        end

        a_list("kundenstamm")
        a_list("checkpayment")
        --[[
                     val1,
                     r.cache,
                     corrid,
                     prior_msg,
                     nc_queue_prop,
                     body_blob,
                     profile
--]]
        local val1 = {} -- ??

        local out_message = val.cache.CF_HIT_DESCRIPTION
        ctx.writer:send_to_finnova_out(out_message, corrid, prior_arg,
                                       val.cache, out_xml, nc_queue_prop)
        --      ctx.writer:commit()
        --      send_(inf,enq,b1,out_xml,corrid,prior_msg,jmsheader)

    end

    local comment = "IsAlert:" .. ss.def(post_data.bot.sign, "0") .. ":" ..
                        ss.def(post_data.bot.score, "0") .. ":" ..
                        ss.def(post_data.bot.MSG, "")
    rstatus:add(bid, corrid, "finish", comment)

    ctx.engine:commit()
    rstatus:commit({lazy = true, lazyKoef = 5})

    if err ~= nil then
        ss.l("detect.error:"..err)
        rstatus:close()
        ss.l("failed:" .. err)
        go_pskill()
    end

end

-- ----------
-- -----------------------------------------------------------------------------------------------------
-- --------------------------main loop------------------------------------------------------------------
-- -----------------------------------------------------------------------------------------------------

local t = tick.now()

local ctx = {}
ctx.reader = reader
ctx.bot2nc = bot2nc
ctx.writer = writer
ctx.engine = engine

ss.l("Start")
while true do
    if analiz:IsStop() then
        ss.l("signal stop")
        break
    end
    main_(ctx)
    if tick.to_sec(tick.now() - t) > 10 * 50 then
        ss.l("wait_count:" .. wait_count)
        t = tick.now()
    end

    if is_restart_new_version then break end

    local err = engine:serv():GetLastError()
    if err ~= "" then
        rstatus:close()
        ss.l("oracle.error:" .. err)
        ss.l("restart after cleep")
        ss.e("sleep sec 15")
        ss.l("abort")
        go_pskill()
        ss.a("1")
    end

end

rstatus:close()

-- -----------------------------------------------------------------------------------------------------
-- ------------------------------finalizing-------------------------------------------------------------
-- -----------------------------------------------------------------------------------------------------
--

ss.l("queueEngine.destroy..")
engine:destroy()

-- destroy Engines
ss.l("postEngines.destroy..")
for k, v in pairs(post_configs.engines) do
    ss.l("DestroyEngine:" .. k)
    v:DestroyEngine()
end
ss.l("postEngines.destroy.end")

if is_restart_new_version then
    ss.l("detect new version.restart service")
    go_pskill()
    ss.l("abort")
    ss.a("1")
    ss.l("after abort")
    ss.e("_ABORT 2")
    ss.l("after abort 2")
end

ss.l("post.finish.end")


