/*
** 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"
	"os"
	"runtime/debug"

	clientcommon "jobarranger2/src/jobarg_server/managers/icon_exec_manager/workers/common"
	"jobarranger2/src/libs/golibs/common"
	"jobarranger2/src/libs/golibs/config_reader/server"
	"jobarranger2/src/libs/golibs/event"
	"jobarranger2/src/libs/golibs/utils"
)

var (
	conn                *common.NetConnection
	runStatusChangeFlag bool
)

func RebootIconClient() (int, error) {
	var (
		executionData common.IconExecutionProcessData
		iconData      common.IconRebootData

		eventData, data common.EventData
		tcpMessage      common.TCPMessage
		jobRunRequest   common.JobRunRequestData

		variableMap map[string]interface{}

		innerJobnetId, innerJobId uint64
		jobnetId, serverId        string
		iconType, agentVersion    int

		err      error
		exitCode int
	)

	// change json file to struct
	executionData, err = clientcommon.GetIconExecData()
	if err != nil {
		return common.JA_JOBEXEC_FAIL, err
	}

	err = utils.Convert(executionData.RunJobData.Data, &iconData)
	if err != nil {
		return common.JA_JOBEXEC_FAIL, fmt.Errorf("failed to convert Data : %w", err)
	}

	// check necessary data
	jobnetId = executionData.RunJobnetData.JobnetID
	if jobnetId == "" {
		return common.JA_JOBEXEC_FAIL, fmt.Errorf("invalid jobnet_id")
	}
	innerJobnetId = executionData.RunJobData.InnerJobnetID
	if innerJobnetId <= 0 {
		return common.JA_JOBEXEC_FAIL, fmt.Errorf("invalid inner_jobnet_id")
	}

	iconType = int(executionData.RunJobData.IconType)
	if iconType != int(common.IconTypeReboot) {
		return common.JA_JOBEXEC_FAIL, fmt.Errorf("invalid icon_type for reboot")
	}

	innerJobId = executionData.RunJobData.InnerJobID
	if innerJobId <= 0 {
		return common.JA_JOBEXEC_FAIL, fmt.Errorf("invalid inner_job_id")
	}

	jobRunRequest.Type = common.AgentJobTypeReboot

	// Converting json to map[]
	variableMap = map[string]interface{}{}
	if err = json.Unmarshal(executionData.RunJobVariableData.BeforeVariable, &variableMap); err != nil {
		return common.JA_JOBEXEC_FAIL, fmt.Errorf("failed to unmarshal Data : %w", err)
	}

	// prepare agent json data and next process data
	data.Event.Name = common.EventAgentJobRun
	data.Event.UniqueKey = common.GetUniqueKey(common.IconExecManagerProcess)
	data.NextProcess.Name = common.AgentManagerProcess

	// inserting tcp message struct
	tcpMessage = common.TCPMessage{}

	tcpMessage.Kind = clientcommon.JobRun
	tcpMessage.Version = clientcommon.Version

	serverId = iconData.ServerID
	if serverId == "" {
		return common.JA_JOBEXEC_FAIL, fmt.Errorf("invalid serverId")
	}
	tcpMessage.ServerID = serverId

	if iconData.HostName == "" {
		return common.JA_JOBEXEC_FAIL, fmt.Errorf("invalid Hostname")
	}

	tcpMessage.Hostname = iconData.HostName

	// checking agent version (JAZ1 agent or JAZ2 agent)
	if agentVersion, _, err = clientcommon.CheckJazVersion(iconData.HostIp, iconData.HostPort, tcpMessage.Hostname, iconData.ServerID); err != nil {
		return common.JA_JOBEXEC_FAIL, err
	}

	//connect tcp and send data
	conn, err = clientcommon.TcpConnect(iconData.HostIp, iconData.HostPort, clientcommon.Timeout)
	if err != nil {
		return common.JA_JOBEXEC_FAIL, err
	}

	// Only extract the data for jaz 1. For jaz 2, data will be sent as it is, and extraction process will be done on agent side.
	if agentVersion == 1 {
		if err := clientcommon.PrepareJobRunRequest(executionData, &jobRunRequest); err != nil {
			return common.JA_JOBEXEC_FAIL, err
		}
	}

	// adding Nextprocess data data
	tcpMessage.Data = jobRunRequest
	data.NextProcess.Data = executionData

	// clean queries and transfer file
	data.ParameterizedQueries = nil
	data.Transfer.Files = nil

	data.TCPMessage = &tcpMessage
	eventData = clientcommon.IconRunDataPrep(string(clientcommon.IconClientJob), executionData)

	exitCode, err = clientcommon.SendAndReceive(conn, data, agentVersion, iconData.Jaz1SupportFlag)
	if err != nil && exitCode != common.JA_JOBRESULT_SUCCEED {
		return exitCode, err
	}

	// create event to change run status if not job abort
	if executionData.RunJobData.MethodFlag != common.MethodAbort {
		err = event.CreateNextEvent(eventData, innerJobnetId, jobnetId, innerJobId)
		if err != nil {
			return common.JA_JOBRESULT_PRE_EXEC_FAIL, err
		}
		runStatusChangeFlag = true
	}

	return exitCode, nil
}

func main() {

	var (
		err      error
		exitCode int
		pid      int
	)

	//catch runtime panic errors
	defer func() {
		if r := recover(); r != nil {
			//output stacktrace
			fmt.Fprintf(os.Stderr, "[RebootIconClient] Runtime panic error occurs in client. error : %s", string(debug.Stack()))
			if runStatusChangeFlag {
				os.Exit(common.JA_JOBRESULT_FAIL)
			}
			os.Exit(common.JA_JOBRESULT_PRE_EXEC_FAIL)
		}
	}()

	// add client_pid in .clientPID file
	pid = os.Getpid()
	err = common.SetClientPid(pid)
	if err != nil {
		fmt.Fprintf(os.Stderr, "[SetClientPid] %v", err)
		os.Exit(common.JA_JOBEXEC_FAIL)
	}

	err = clientcommon.ParseArgs()
	if err != nil {
		fmt.Fprintf(os.Stderr, "[ParseArgs] %v", err)
		os.Exit(common.JA_JOBEXEC_FAIL)
	}

	/*
	 // Since the client does not accept a configuration parameter,
	 // manually assign UnixSockParentDir and TmpDir.
	*/
	server.Options.UnixSockParentDir = clientcommon.UdsDirpath
	server.Options.TmpDir = clientcommon.TmpDirPath

	exitCode, err = RebootIconClient()
	if err != nil {
		fmt.Fprintf(os.Stderr, "[RebootIconClient] %v", err)
	}
	os.Exit(exitCode)

}
