/*
** Job Arranger for ZABBIX
** Copyright (C) 2025 Daiwa Institute of Research Ltd. All Rights Reserved.
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
**/
package builder

import (
	"encoding/json"
	"fmt"
	"time"

	"jobarranger2/src/libs/golibs/common"
	"jobarranger2/src/libs/golibs/config_reader/server"
	"jobarranger2/src/libs/golibs/database"
)

// ************************************************************************************************

// Job related query builders starts here
func GetJobStatusChangeQuery(
	innerJobId uint64,
	status common.StatusType,
	methodFlag common.JobRunMethod,
	runCnt int,
	start int, end int,
) (common.ParameterizedQuery, common.ParameterizedQuery) {

	ts := time.Now().Unix()
	timestampStr := fmt.Sprintf("%d", ts)

	var strMethodFlag, strRunCnt, strStart, strEnd string

	switch methodFlag {
	case -1:
		strMethodFlag = ""
	default:
		strMethodFlag = fmt.Sprintf(", method_flag = %d", methodFlag)
	}

	switch runCnt {
	case 0:
		strRunCnt = ""
	default:
		strRunCnt = fmt.Sprintf(", run_count = %d", runCnt)
	}

	switch start {
	case 0:
		strStart = ", start_time = 0"
	case 1:
		strStart = fmt.Sprintf(", start_time = %s", timestampStr)
	default:
		strStart = ""
	}

	switch end {
	case 0:
		strEnd = ", end_time = 0"
	case 1:
		strEnd = fmt.Sprintf(", end_time = %s", timestampStr)
	default:
		strEnd = ""
	}

	sql := fmt.Sprintf("select status from %s where inner_job_id = ? for update", common.Ja2RunJobTable)
	lockPQ := common.GetPQ(sql, innerJobId)

	updateSQL := fmt.Sprintf(
		"update ja_2_run_job_table set status = ?%s%s%s%s where inner_job_id = ?",
		strMethodFlag, strRunCnt, strStart, strEnd,
	)
	updatePQ := common.GetPQ(updateSQL, status, innerJobId)

	return lockPQ, updatePQ
}

func GetRunJobMethodFlagChangeQuery(innerJobId uint64, methodFlag common.JobRunMethod) (common.ParameterizedQuery, common.ParameterizedQuery) {
	lockSQL := fmt.Sprintf("select method_flag from ja_2_run_job_table " +
		"where inner_job_id = ? for update")
	lockPQ := common.GetPQ(lockSQL, innerJobId)

	updateSQL := fmt.Sprintf("UPDATE ja_2_run_job_table set method_flag = ? where inner_job_id = ?")
	updatePQ := common.GetPQ(updateSQL, methodFlag, innerJobId)

	return lockPQ, updatePQ
}

func GetRunJobRetryCountIncrementQuery(innerJobId uint64, retryCount int) (common.ParameterizedQuery, common.ParameterizedQuery) {
	lockSQL := fmt.Sprintf("select retry_count from %s where inner_job_id = ? for update", common.Ja2RunJobTable)
	lockPQ := common.GetPQ(lockSQL, innerJobId)

	updateSQL := fmt.Sprintf("UPDATE %s set retry_count = ? where inner_job_id = ?", common.Ja2RunJobTable)
	updatePQ := common.GetPQ(updateSQL, retryCount, innerJobId)

	return lockPQ, updatePQ
}

func GetRunJobVariableInsertQuery(runJobVaribaleData common.RunJobVariableTable) (common.ParameterizedQuery, error) {
	var query string
	var PQ common.ParameterizedQuery

	beforeVariable, err := json.Marshal(runJobVaribaleData.BeforeVariable)
	if err != nil {
		return PQ, err
	}
	switch server.Options.DBType {
	case database.MysqlDBType, database.MariaDBType:
		query = fmt.Sprintf("INSERT INTO %s "+
			"(seq_no, inner_job_id, inner_jobnet_id, before_variable) "+
			"VALUES (?, ?, ?, ?) "+
			"ON DUPLICATE KEY UPDATE before_variable = VALUES(before_variable);",
			common.Ja2RunJobVariableTable)
		PQ = common.GetPQ(query,
			runJobVaribaleData.SeqNo,
			runJobVaribaleData.InnerJobID,
			runJobVaribaleData.InnerJobnetID,
			string(beforeVariable),
		)

	case database.PostgresDBType:
		query = fmt.Sprintf("INSERT INTO %s "+
			"(seq_no, inner_job_id, inner_jobnet_id, before_variable) "+
			"VALUES (?, ?, ?, ?) "+
			"ON CONFLICT (seq_no, inner_job_id, inner_jobnet_id) "+
			"DO UPDATE SET before_variable = EXCLUDED.before_variable;",
			common.Ja2RunJobVariableTable)
		PQ = common.GetPQ(query,
			runJobVaribaleData.SeqNo,
			runJobVaribaleData.InnerJobID,
			runJobVaribaleData.InnerJobnetID,
			string(beforeVariable),
		)
	}

	return PQ, err
}

func GetHostLockQuery(host string) (common.ParameterizedQuery, common.ParameterizedQuery) {

	lockSQL := "select lock_host_name from ja_2_host_lock_table " +
		"where lock_host_name = 'HOST_LOCK_RECORD' for update"
	lockPQ := common.GetPQ(lockSQL)

	insertSQL := "insert into ja_2_host_lock_table (lock_host_name) values (?)"
	insertPQ := common.GetPQ(insertSQL, host)

	return lockPQ, insertPQ
}

func GetRunTimeoutTableUpdateAbortFlagQuery(innerJobId uint64, flag bool) (common.ParameterizedQuery, common.ParameterizedQuery) {
	flagStr := 0
	if flag {
		flagStr = 1
	}

	lockSQL := fmt.Sprintf("select abort_flag from %s where inner_job_id = ? for update", common.Ja2RunTimeOutTable)
	lockPQ := common.GetPQ(lockSQL, innerJobId)

	updateSQL := fmt.Sprintf("UPDATE %s SET abort_flag = ? WHERE inner_job_id = ?", common.Ja2RunTimeOutTable)
	updatePQ := common.GetPQ(updateSQL, flagStr, innerJobId)

	return lockPQ, updatePQ
}
