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

seclevel = CreateSecLevel("release")

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

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=""})

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

end

local prop_loader = finn_serv.CreatePropLoader()

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

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

function go_pskill()
    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

local function createMap_(arr)
    local res = {}
    for i = 1, #arr do
        local a = arr[i]
        res[a[1]] = a[2]
    end
    return res
end

local normalizateMap_ = createMap_({
    {"y", "Y"}, {"1", "Y"}, {"n", "N"}, {"0", "N"}
})

local function isempty_(v)
    if v == nil then return true end
    if v == "" then return true end
    return false
end

local function add_(f, s, comment)
    if isempty_(f) and isempty_(s) then return nil end

    if isempty_(f) == false and isempty_(s) == false then
        -- first,second
        return f .. "," .. ss.def(comment, "") .. ss.def(s, "")
    end

    -- first
    if isempty_(s) then return f end
    -- only second
    return ss.def(comment, "") .. ss.def(s, "")
end

-- ----------------------------
function GetBid(cache)
    local bid = nil
    if cache.CF_PAYMENT_ID ~= nil then
        bid = cache.CF_PAYMENT_ID
        ss.l("std.bid:" .. bid)
    elseif cache.CF_CORRELATION_ID ~= nil then
        bid = cache.CF_CORRELATION_ID
        ss.l("Kundenstamm.bid:" .. bid)
    else
        ss.l("failed not found bid!!!:")
    end
    return bid
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, jheader)
    b1:write(data)

    local o = {}

    local prior_arg = 1
    local expiration_arg = 5 * 180

    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()
engine = CreateQueueEngine()
engine:init()

local input_queue_name = global_oracle_setting.internal.release_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_release_header(nc_queue_prop, bid, corrid, data_ex, err,
                                      cache)
    --[[    local jmsheader=header

    jmsheader:clear()

    jmsheader:set_type("JmsBytesMessage")
    jmsheader:set_appid("releaseService")
    jmsheader:set_protocol(0)
    jmsheader:set_groupid('lex')
    jmsheader:set_groupseq(1)
--]]
    local ret_code = "0"

    if err ~= nil then ret_code = "1" end

    add_item(nc_queue_prop, 'CF_PAYMENT_ID', bid);

    add_item(nc_queue_prop, "MSG_UUID", corrid);
    add_item(nc_queue_prop, "CF_RESP_CODE", ret_code)
    add_item(nc_queue_prop, "CF_RESP_MESSAGE", ss.def(err, ""))
    add_item(nc_queue_prop, "F_BANK_ID", ss.def(cache.F_BANK_ID))
    add_item(nc_queue_prop, "F_RECORD_TYPE", "KD04")

    --    jmsheader:show()

end

-- ---------------------------------------
local function param_from_sql_(pwd, db_field, sql, def_value)
    ss.s("tmp_ret", def_value)
    analiz:load_from_sql("tmp_ret", db_field, pwd, sql)
    local ret = ss.g("tmp_ret")
    return ret
end
-- ---------------------------------------
local function execSql_(pwd, sql)
    ss.s("tmp_sql", sql)
    analiz:execute("echo EXEC_SQL " .. pwd .. " $(tmp_sql)")
    analiz:execute("ANALIZ_SAFE_EXECUTE ret tmp_emessage EXEC_SQL " .. pwd ..
                       " $(tmp_sql)")
    local err = nil
    local emessage = ss.g("tmp_emessage")
    if emessage ~= "" then
        ss.l("failed:" .. emessage)
        err = emessage
    end
    return err
end
-- ---------------------------------------
local function map_from_sql_(pwd, map_obj, sql)
    map_obj:clear()
    ss.s("tmp_sql", sql)
    analiz:execute(
        "echo ADD_MAP_FROM_SQL_SILEN " .. map_obj.name .. " " .. pwd ..
            " $(tmp_sql)")
    analiz:execute(
        "ANALIZ_SAFE_EXECUTE ret tmp_emessage ADD_MAP_FROM_SQL_SILEN " ..
            map_obj.name .. " " .. pwd .. " $(tmp_sql)")
    local err = nil
    local emessage = ss.g("tmp_emessage")
    if emessage ~= "" then
        ss.l("failed:" .. emessage)
        err = emessage
    end
    return err
end

-- -------------------------------------------------------------------------
local function to_ms_string_arg(val)
    if val == "" then return "NULL" end
    val = ss.ReplaceValue(val, "'", "''")
    return "'" .. val .. "'"
end

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

-- -------------------------------------------------------------------------
-- -------------------------------------------------------------------------
-- -------------------------------------------------------------------------
local function ChangeOnlineAlertStatusWithComment(bid, status, user, comment)
    local sql = [[declare @dt datetime=GetDate()
EXEC    [dbo].[ChangeOnlineAlertStatusWithComment]
         ']] .. bid .. [[', --@businesscaseid
         ]] .. status .. [[, --status
          ]] .. ss.def(user, "sa") .. [[ ,  --@username
         @dt,-- GetDate(),
        ']] .. ss.def(comment, "") .. [[']]

    return execSql_("PLIB", sql)
end

local function AddOnlineAlertToWhitelist(bid, user, comment)
    local sql = [[declare @dt datetime=GetDate()
           EXEC    [dbo].[AddOnlineAlertToWhitelist]
        ']] .. bid .. [[',--@businesscaseid 
        NULL, -- @list_id
        ']] .. ss.def(user, "sa") .. [[', --@username 
        @dt, -- @date (GetDate())
        ']] .. ss.def(comment, "") .. [[' --@text]]
    return execSql_("PLIB", sql)
end

function releaseImpl(ctx, cmd)

    time_stat:startProcessing()
    ss.l("_____________________________________________________________rs")
    local pwd = "PLIB"

    local bid = GetBid(cmd.cache)

    local is_error = false
    local e_message = ""

    if bid == nil then
        ss.l("not found bid")
        bid = '<nil>'
        is_error = true
        e_message = add_(e_message, "bid is NULL")
    end

    -- ao:
    if cmd.cache.F_COMM_TYPE == '11' and cmd.cache.F_RELEASE_FLAG == nil then
        cmd.cache.F_RELEASE_FLAG = 'Y'
    end

    local function normalizateFlag(v)
        local r = normalizateMap_[v]
        if r ~= nil then
            ss.l("normalizateFlag:" .. v .. " :" .. r)
            return r
        end
        return v
    end

    cmd.cache.F_WHITELIST_FLAG = normalizateFlag(cmd.cache.F_WHITELIST_FLAG)
    cmd.cache.F_RELEASE_FLAG = normalizateFlag(cmd.cache.F_RELEASE_FLAG)

    local whitelist_flag = ss.def(cmd.cache.F_WHITELIST_FLAG, "<nil>")
    local release_flag = ss.def(cmd.cache.F_RELEASE_FLAG, "<nil>")
    local F_RELEASE_REASON = ss.def(cmd.cache.F_RELEASE_REASON, "")
    local F_RELEASE_DATE = ss.def(cmd.cache.F_RELEASE_DATE, "")
    local F_USER_ID = ss.def(cmd.cache.F_USER_ID, "")

    --  VALIDATE . START
    do
        local function CheckYN(v)
            if v == "Y" or v == "N" then return true end
            return false
        end

        local check_white = CheckYN(cmd.cache.F_WHITELIST_FLAG)
        local check_release = CheckYN(cmd.cache.F_RELEASE_FLAG)

        if not (check_white) then
            is_error = true
            e_message = add_(e_message,
                             "F_WHITELIST_FLAG is not valid value:" ..
                                 ss.def(cmd.cache.F_WHITELIST_FLAG, "<nul>"))
        end
        if not (check_release) then
            is_error = true
            e_message = add_(e_message, "F_RELEASE_FLAG is not valid value:" ..
                                 ss.def(cmd.cache.F_RELEASE_FLAG, "<nul>"))
        end

        if is_error then
            ss.l("validate.failed.message:" .. e_message)
            return {prop = {WHITE_LIST_ID = -1}}, e_message
        end
    end

    -- VALIDATE. END

    local err = nil
    local desc = "Generate " .. analiz:get("SERVICE_NAME") .. "_" ..
                     analiz:get("UNIQUE_IDENTIFIER") .. " CF_PAYMENT_ID:" .. bid

    local tsql = "declare @dt datetime\n"
    tsql = tsql .. "set @dt= convert(datetime," ..
               to_ms_string_arg(ctx.engine:serv():Convert_DT_to_mssqlDT(
                                    F_RELEASE_DATE)) .. ",120)\n"

    tsql = tsql .. "EXEC [dbo].[Finnova_Release]   @BID_ID=" ..
               to_ms_string_arg(bid)
    tsql = tsql .. ", @WHITELIST_FLAG=" .. to_ms_string_arg(whitelist_flag)
    tsql = tsql .. ", @F_RELEASE_FLAG=" .. to_ms_string_arg(release_flag)
    tsql = tsql .. ", @F_RELEASE_REASON=" .. to_ms_string_arg(F_RELEASE_REASON)
    tsql = tsql .. ", @F_RELEASE_DATE=@dt"
    tsql = tsql .. ", @F_USER_ID=" .. to_ms_string_arg(F_USER_ID)
    tsql = tsql .. ", @THRESHOLD=" .. finn_serv.score_threshold

    local m = analiz:map_new("wl_map")
    add_(err, map_from_sql_(pwd, m, tsql))

    m:show()

    local id = m:get("WHITELIST_ID")
    local ret_code = m:get("RESP_CODE")
    local ret_message = m:get("RESP_MESSAGE")
    ss.l("WHITE_LIST_ID:" .. ss.def(id, "<nil>"))

    if ret_code == "failed" then err = add_(err, ret_message) end

    time_stat:endProcessing()
    time_stat:show()

    return {prop = {WHITE_LIST_ID = id}}, err
end
-- -------------------------------------------------------------------------
-- -------------------------------------------------------------------------
-- -------------------------------------------------------------------------

local wait_count = 0

-- ----------main loop
function main_(ctx)
    clear_last_info()
    deadline:DeadLineLazy("wait", deadline.time.WAIT)
    internal_state = "read message..."
    local msg = ctx.reader:read();

    if msg:isnil() then
        wait_count = wait_count + 1
        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

    local corrid = msg:getCorrelation()
    last_corrid = corrid
    last_prior = prior

    --   local obj = msg:getObj();
    --   local val =   prop_loader:parse_all(obj)

    local val = msg:property()
    ss.l("corrid:" .. ss.def(corrid, "<nil>"))
    ss.l(ss.toJson(val.cache))
    last_cache = val.cache

    local cid = val.cache.CORRELATION_ID
    if cid ~= nil then
        profile = corrid
        corrid = cid
    else
        profile = "default_profile"
    end

    last_corrid = corrid

    deadline:DeadLine("processing", deadline.time.PROCESSING)

    local bid = GetBid(val.cache)
    last_bid = bid

    if bid == nil then bid = "BID is NULL" end
    rstatus:add(bid, corrid, "release.start", "todo")

    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

    internal_state = "processing"
    local ret, err = releaseImpl(ctx, val)
    rstatus:add(bid, corrid, "finish", "todo")

    internal_state = "calc out message..."

    local out_xml = "todo:release_finnova_message"
    ss.l("out xml:" .. out_xml)
    local nc_queue_prop = {}

    setting_release_header(nc_queue_prop, bid, corrid, ret, err, val.cache)

    local out_message = "???"

    internal_state = "send to out..."
    ctx.writer:send_to_finnova_out(out_message, corrid, prior_arg, val.cache,
                                   out_xml, nc_queue_prop)

end

-- ----------
-- -----------------------------------------------------------------------------------------------------
-- --------------------------main loop------------------------------------------------------------------
-- -----------------------------------------------------------------------------------------------------
local ctx = {}
ctx.reader = reader
ctx.writer = writer
ctx.engine = engine
local t = tick.now()

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

    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

-- -----------------------------------------------------------------------------------------------------
-- ------------------------------finalizing-------------------------------------------------------------
-- -----------------------------------------------------------------------------------------------------
rstatus:close()
ss.l("queueEngine.destroy..")
engine:destroy()

-- deq:free()
-- engine:free()
-- oracle_engine:Cleanup()


