/*
** 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 main

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

	"jobarranger2/src/libs/golibs/common"
	"jobarranger2/src/libs/golibs/database"
	"jobarranger2/src/libs/golibs/utils"
)

// jaSetValueJobnetBefore constructs and appends an INSERT query for jobnet variables.
// It builds a map of variable names and values based on the provided JobnetData and valueNames.
func jaSetValueJobnetBefore(valueNames []string, data common.JobnetRunData, dbconn database.DBConnection, queries ...common.ParameterizedQuery) ([]common.ParameterizedQuery, error) {
	funcname := "jaSetValueJobnetBefore"
	JobnetRunLogger.JaLog("JAJOBNETRUN400001", funcname) // Entering function

	valueMap := make(map[string]any)
	commandVariableMap := make(map[string]any)
	var (
		currentTime, currentTimeTz,
		bootTime, bootTimeTz,
		scheduleTime, scheduleTimeTz uint64
		timezone      string
		err           error
		count         int
		runByExternal bool
		query         string
		paramQuery    common.ParameterizedQuery
		calendarLoc   *time.Location
	)
	serverTimeZone := GetLocalIanaTimezone()
	calendarLoc = MustLoadLocationOrDefault(timezone, serverTimeZone)

	currentTime = FormatTimeToUint64(time.Now(), "20060102150405")
	bootTime = FormatTimeToUint64(time.Now(), "200601021504")

	currentTimeTz = currentTime
	bootTimeTz = bootTime
	timezone = serverTimeZone

	if data.MainFlag == common.JA_JOBNET_MAIN_FLAG_MAIN {
		if data.ScheduledTime != 0 {
			if data.Timezone != "" {
				timezone = data.Timezone
			}
			calendarLoc = MustLoadLocationOrDefault(timezone, serverTimeZone)
			currentTimeTz = FormatTimeToUint64(time.Now().In(calendarLoc), "20060102150405")
			bootTimeTz = FormatTimeToUint64(time.Now().In(calendarLoc), "200601021504")
			scheduleTime = ConvertUnixToTZ(int64(data.ScheduledTime), serverTimeZone)
			scheduleTimeTz = ConvertUnixToTZ(int64(data.ScheduledTime), timezone)
		}
	}

	if data.RunType == JA_JOBNET_RUN_TYPE_TEST {
		//to get the actual run time for test= now - crated + testRuntime
		t := time.Unix(time.Now().Unix()-int64(data.CreatedDate)+int64(data.TestRunTime), 0)
		currentTime = FormatTimeToUint64(t, "20060102150405")
		bootTime = FormatTimeToUint64(t, "200601021504")
		currentTimeTz = FormatTimeToUint64(t, "20060102150405")
		bootTimeTz = FormatTimeToUint64(t, "200601021504")
	}

	for _, name := range valueNames {
		switch name {
		case CURRENT_TIME:
			valueMap[CURRENT_TIME] = currentTime
		case CURRENT_TIME_TZ:
			valueMap[CURRENT_TIME_TZ] = currentTimeTz
		case JOBNET_BOOT_TIME:
			valueMap[JOBNET_BOOT_TIME] = bootTime
		case JOBNET_BOOT_TIME_TZ:
			valueMap[JOBNET_BOOT_TIME_TZ] = bootTimeTz
		case JOBNET_SCHEDULED_TIME:
			valueMap[JOBNET_SCHEDULED_TIME] = scheduleTime
		case JOBNET_SCHEDULED_TIME_TZ:
			valueMap[JOBNET_SCHEDULED_TIME_TZ] = scheduleTimeTz
		case TIMEZONE:
			valueMap[TIMEZONE] = timezone
		case JOBNET_ID:
			valueMap[JOBNET_ID] = data.JobnetID
		case JOBNET_NAME:
			valueMap[JOBNET_NAME] = data.JobnetName
		case USER_NAME:
			valueMap[USER_NAME] = data.UserName
		case MANAGEMENT_ID:
			valueMap[MANAGEMENT_ID] = data.InnerJobnetId
		}
	}

	if dbconn != nil {
		conditions := []string{}

		conditions = append(conditions, fmt.Sprintf("inner_jobnet_id = %d", data.InnerJobnetId))
		count, err = utils.GetRecordCountFromTableByColumn(dbconn, common.Ja2RunJobnetVariableTable, conditions)
		if count != 0 {
			runByExternal = true

			var runJobnetVariableTable common.RunJobnetVariableTable
			runJobnetVariableTable, err = utils.GetJobnetVariableDataByInnerJobnetId(dbconn, data.InnerJobnetId)
			if err != nil {
				JobnetRunLogger.JaLog("JAJOBNETRUN400003", funcname, err.Error())
				return []common.ParameterizedQuery{}, fmt.Errorf("%s", err.Error())
			}

			commandVariableMap, err = utils.RawJsonToMapStringAny(runJobnetVariableTable.BeforeVariable)
			if err != nil {
				JobnetRunLogger.JaLog("JAJOBNETRUN400003", funcname, err.Error())
				return []common.ParameterizedQuery{}, fmt.Errorf("%s", err.Error())
			}

		} else {
			count += 1
		}
		if err != nil {
			JobnetRunLogger.JaLog("JAJOBNETRUN400003", funcname, err.Error())
			return []common.ParameterizedQuery{}, fmt.Errorf("%s", err.Error())
		}
	}

	//combine with variables from external command
	maps.Copy(valueMap, commandVariableMap)

	// Merge custom/override variables if present
	if data.JobnetIconBeforeVariable != nil {
		if beforeVars := UnmarshalMap(data.JobnetIconBeforeVariable); beforeVars != nil {
			for k, v := range beforeVars {
				if _, exists := valueMap[k]; !exists {
					valueMap[k] = v
				}
			}
		}
	}

	valueJSON, err := json.Marshal(valueMap)
	if err != nil {
		JobnetRunLogger.JaLog("JAJOBNETRUN400003", funcname, err.Error())
		return []common.ParameterizedQuery{}, fmt.Errorf("%s", err.Error())
	}

	if runByExternal {
		query = fmt.Sprintf(
			`UPDATE %s set before_variable = ? WHERE seq_no = ? AND inner_jobnet_id = ?;`,
			common.Ja2RunJobnetVariableTable,
		)
		paramQuery = common.GetPQ(query, string(valueJSON), count, data.InnerJobnetId)

	} else {
		query = fmt.Sprintf(
			`INSERT INTO %s (seq_no,inner_jobnet_id,before_variable) VALUES (?,?,?);`,
			common.Ja2RunJobnetVariableTable,
		)
		paramQuery = common.GetPQ(query, count, data.InnerJobnetId, string(valueJSON))

	}

	return append(queries, paramQuery), nil
}
