/*
** 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 (
	"fmt"
	"io"
	"os"
	"path/filepath"
	"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/uds"
	"jobarranger2/src/libs/golibs/utils"
)

var (
	runStatusChangeFlag bool
	zbxIconData         common.IconLinkData
)

func zabbixIconClient() (int, error) {
	var (
		eventData     common.EventData
		executionData common.IconExecutionProcessData
		zbxResultData common.ZabbixIconResult

		innerJobnetMainId, innerJobId uint64
		runCount, methodFlag          int
		flag                          bool
	)

	// read data file
	fd, err := os.Open(clientcommon.DataFilePath)
	if err != nil {
		return common.JA_JOBEXEC_FAIL, err
	}
	defer fd.Close()

	eventDataBytes, err := io.ReadAll(fd)
	if err != nil {
		return common.JA_JOBEXEC_FAIL, err
	}

	if err = utils.UnmarshalEventData(eventDataBytes, &eventData); err != nil {
		return common.JA_JOBEXEC_FAIL, err
	}

	//get execution data
	if err = utils.Convert(eventData.NextProcess.Data, &executionData); err != nil {
		return common.JA_JOBEXEC_FAIL, err
	}

	// Check necessary data
	innerJobnetMainId = executionData.RunJobData.InnerJobnetMainID
	if innerJobnetMainId <= 0 {
		return common.JA_JOBEXEC_FAIL, fmt.Errorf("invalid inner_jobnet_main_id")
	}
	innerJobId = executionData.RunJobData.InnerJobID
	if innerJobId <= 0 {
		return common.JA_JOBEXEC_FAIL, fmt.Errorf("invalid inner_job_id")
	}
	runCount = executionData.RunJobData.RunCount
	if runCount < 0 {
		return common.JA_JOBEXEC_FAIL, fmt.Errorf("invalid run_count")
	}
	methodFlag = int(executionData.RunJobData.MethodFlag)
	if methodFlag < 0 {
		return common.JA_JOBEXEC_FAIL, fmt.Errorf("invalid method_flag")
	}

	// check flag file exist or not
	flag, err = event.CheckRunCountFile(clientcommon.RunCountFolderPath, innerJobnetMainId, innerJobId, runCount, methodFlag)
	if err != nil {
		return common.JA_JOBEXEC_FAIL, fmt.Errorf("failed to create flag file : %w", err)
	} else if !flag { // flag == false (nil && false)
		return common.JA_JOBEXEC_IGNORE, nil
	}

	// get zbx icon data
	if err = utils.Convert(executionData.RunJobData.Data, &zbxIconData); err != nil {
		return common.JA_JOBEXEC_FAIL, err
	}

	// send data to zabbix link manager
	udsClient := uds.CreateUdsClient(filepath.Join(clientcommon.UdsDirpath, common.ZabbixLinkManagerSockFile), 1)
	udsConn, err := udsClient.Connect()
	if err != nil {
		return common.JA_JOBEXEC_FAIL, err
	}

	if err := udsConn.Send(eventData); err != nil {
		return common.JA_JOBEXEC_FAIL, err
	}

	runEventData := clientcommon.IconRunDataPrep(string(clientcommon.IconClientLink), executionData)

	// create icon run event
	if err = event.CreateNextEvent(runEventData, executionData.RunJobData.InnerJobnetID, executionData.JobnetRunData.JobnetID, executionData.RunJobData.InnerJobID); err != nil {
		return common.JA_JOBRESULT_PRE_EXEC_FAIL, err
	}

	runStatusChangeFlag = true

	// flag file creation
	err = event.CreateRunCountFile(clientcommon.RunCountFolderPath, innerJobnetMainId, innerJobId, runCount)
	if err != nil {
		return common.JA_JOBRESULT_FAIL, fmt.Errorf("failed to create flag file : %w", err)
	}

	// receive result from zabbix link
	resultMap, err := udsConn.Receive()
	if err != nil {
		return common.JA_JOBRESULT_FAIL, err
	}

	if err = utils.Convert(resultMap, &zbxResultData); err != nil {
		return common.JA_JOBRESULT_FAIL, err
	}

	if zbxResultData.Error != "" {
		// write error info
		return common.JA_JOBRESULT_FAIL, fmt.Errorf("%s", zbxResultData.Error)
	}

	zbxIconData.ZBXDataType = zbxResultData.Output.ZBXDataType
	zbxIconData.ZBXLastStatus = zbxResultData.Output.ZBXLastStatus
	zbxIconData.ZBXLatestData = zbxResultData.Output.ZBXLatestData

	// write result in data file
	if err := clientcommon.WriteStructToFD3(zbxIconData); err != nil {
		fmt.Fprintln(os.Stderr, "Failed to write last status to fd3:", err)
		return common.JA_JOBRESULT_FAIL, err
	}

	return common.JA_JOBRESULT_SUCCEED, nil
}

func main() {

	//catch runtime panic errors
	defer func() {
		if r := recover(); r != nil {
			//output stacktrace
			fmt.Fprintf(os.Stderr, "[ZabbixIconClient] 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, "[ZabbixIconClient] %v", err)
		os.Exit(common.JA_JOBEXEC_FAIL)
	}

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

	server.Options.UnixSockParentDir = clientcommon.UdsDirpath
	server.Options.TmpDir = clientcommon.TmpDirPath

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

	}
	os.Exit(exitCode)
}
