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

import (
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"strconv"
	"time"

	"jobarranger2/src/libs/golibs/common"
	"jobarranger2/src/libs/golibs/config_reader/server"
	"jobarranger2/src/libs/golibs/event"
	"jobarranger2/src/libs/golibs/logger/logger"
	"jobarranger2/src/libs/golibs/utils"
)

var execManagerInPath, execManagerRunPath, clientDataPath string
var execManagerEndPath, execManagerErrorPath, execManagerUdsPath string
var iconExecManagerStr string

func loadCommonVars() {
	iconExecManagerStr = string(common.IconExecManagerFolder)

	execManagerInPath = filepath.Join(
		server.Options.TmpDir, iconExecManagerStr, "in")
	execManagerRunPath = filepath.Join(
		server.Options.TmpDir, iconExecManagerStr, "run")
	clientDataPath = filepath.Join(
		server.Options.TmpDir, iconExecManagerStr, "client_data")
	execManagerEndPath = filepath.Join(
		server.Options.TmpDir, iconExecManagerStr, "end")
	execManagerErrorPath = filepath.Join(
		server.Options.TmpDir, iconExecManagerStr, "error")
	execManagerUdsPath = filepath.Join(
		server.Options.UnixSockParentDir, common.IconExecManagerSockFile)
}

func GetIconExecutionProcessData(transactionFilePath string) (*common.IconExecutionProcessData, error) {
	bytes, err := os.ReadFile(transactionFilePath)
	if err != nil {
		return nil, err
	}

	var eventData common.EventData

	err = utils.UnmarshalEventData(bytes, &eventData)
	if err != nil {
		return nil, err
	}

	processData, ok := eventData.NextProcess.Data.(common.IconExecutionProcessData)
	if !ok {
		return nil, fmt.Errorf(
			"type cast failed, expected type: common.IconExecutionProcessData, data: %v", eventData.NextProcess.Data)
	}

	return &processData, nil
}

func setRunErr(message string, processData common.IconExecutionProcessData) error {
	if processData.JobResult.PreExecFailFlag {
		processData.JobResult.Result = common.JA_JOBRESULT_PRE_EXEC_FAIL
	} else {
		processData.JobResult.Result = common.JA_JOBRESULT_FAIL
	}
	processData.JobResult.Message = message
	processData.JobResult.JobRunRequestData.JobID = strconv.FormatUint(processData.RunJobData.InnerJobID, 10)

	eventData := common.EventData{
		Event: common.Event{
			Name:      common.EventIconResultUpdate,
			UniqueKey: common.GetUniqueKey(common.IconExecManagerProcess),
		},
		NextProcess: common.NextProcess{
			Name: common.IconResultManagerProcess,
			Data: processData,
		},
	}

	return event.CreateNextEvent(
		eventData, processData.RunJobData.InnerJobnetID, processData.RunJobnetData.JobnetID, processData.RunJobData.InnerJobID,
	)
}

func setExecFail(message string, processData common.IconExecutionProcessData) error {
	// 5s interval for icon retry process
	time.Sleep(5 * time.Second)

	if processData.JobResult.Message != "" {
		processData.JobResult.Message += "\n"
	}
	processData.JobResult.Message += message
	processData.JobResult.Result = common.JA_JOBRESULT_FAIL
	processData.JobResult.JobRunRequestData.JobID = strconv.FormatUint(processData.RunJobData.InnerJobID, 10)

	flowProcessData := common.FlowProcessData{
		InnerJobnetId: processData.RunJobData.InnerJobnetID,
		JobnetId:      processData.RunJobnetData.JobnetID,
		InnerJobId:    processData.RunJobData.InnerJobID,
		IconType:      processData.RunJobData.IconType,
		Data:          processData,
	}

	eventData := common.EventData{
		Event: common.Event{
			Name:      common.EventIconRetry,
			UniqueKey: common.GetUniqueKey(common.IconExecManagerProcess),
		},
		NextProcess: common.NextProcess{
			Name: common.FlowManagerProcess,
			Data: flowProcessData,
		},
	}

	return event.CreateNextEvent(eventData, processData.RunJobData.InnerJobnetID, "", processData.RunJobData.InnerJobID)
}

func cleanupEventData(eventData common.EventData, log logger.Logging) error {
	// move event file to end folder
	for _, file := range eventData.Transfer.Files {
		fileName := filepath.Base(file.Source)

		err := os.Rename(
			file.Source,
			filepath.Join(execManagerEndPath, fileName),
		)
		if err != nil {
			log.JaLog("JAEXEC300001", log.InnerJobnetID, log.InnerJobID,
				log.JobnetID, log.JobID, eventData.Event.Name, err.Error())
		}
	}

	log.JaLog("JAEXECEND400002", log.InnerJobnetID, log.InnerJobID,
		log.JobnetID, log.JobID)

	return nil
}

func cleanupEventDataDest(eventData common.EventData, log logger.Logging) error {
	// move event file to end folder
	for _, file := range eventData.Transfer.Files {
		fileName := filepath.Base(file.Source)

		err := os.Rename(
			file.Destination,
			filepath.Join(execManagerEndPath, fileName),
		)
		if err != nil {
			log.JaLog("JAEXEC300001", log.InnerJobnetID, log.InnerJobID,
				log.JobnetID, log.JobID, eventData.Event.Name, err.Error())
		}
	}

	log.JaLog("JAEXECEND400002", log.InnerJobnetID, log.InnerJobID,
		log.JobnetID, log.JobID)

	return nil
}

func cleanUpClientData(transactionFileId, destFolder string) error {
	var err error

	for _, ext := range common.ClientExts {
		src := filepath.Join(clientDataPath, transactionFileId+ext)
		dst := filepath.Join(destFolder, transactionFileId+ext)

		if tempErr := os.Rename(src, dst); tempErr != nil {
			err = errors.Join(err, tempErr)
		}
	}

	return err
}

func cleanupTransactionFiles(exitCode int, transationFileId string) error {
	var err error
	destPath := execManagerErrorPath

	if exitCode == common.JA_JOBRESULT_SUCCEED {
		destPath = execManagerEndPath
	}

	err = cleanUpClientData(transationFileId, destPath)
	os.Rename(filepath.Join(execManagerInPath, transationFileId+".json"),
		filepath.Join(destPath, transationFileId+".json"),
	)
	os.Rename(filepath.Join(execManagerRunPath, transationFileId+".json"),
		filepath.Join(destPath, transationFileId+".json"),
	)

	return err
}

func getIconData[T any](data []byte) (T, error) {
	var iconData T

	err := json.Unmarshal(data, &iconData)
	if err != nil {
		return iconData, fmt.Errorf("json.Unmarshal failed. resultMap[.data]: %s, error:%s", string(data), err.Error())
	}

	return iconData, nil
}
