/*
** 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"
	"fmt"
	"os"
	"syscall"
	"time"

	"jobarranger2/src/libs/golibs/common"
	"jobarranger2/src/libs/golibs/event"
	"jobarranger2/src/libs/golibs/logger/logger"
)

func timerIconAbort(processData common.IconExecutionProcessData) error {
	fmt.Println("timerIconAbort is called")

	var quries []common.ParameterizedQuery

	quries = append(quries, common.GetPQ(`
		select abort_flag from ja_2_run_timeout_table 
		where inner_jobnet_id = ? and inner_job_id = ? for update
		`,
		processData.RunJobData.InnerJobnetID, processData.RunJobData.InnerJobID,
	))

	quries = append(quries, common.GetPQ(`
		update ja_2_run_timeout_table
		set abort_flag = true
		where inner_jobnet_id = ? and inner_job_id = ?
		`,
		processData.RunJobData.InnerJobnetID, processData.RunJobData.InnerJobID,
	))

	eventData := common.EventData{
		Event: common.Event{
			Name:      common.EventTwoWayIconRun,
			UniqueKey: common.GetUniqueKey(common.IconExecManagerProcess),
		},
		NextProcess: common.NextProcess{
			Name: common.DBSyncerManagerProcess,
			Data: processData,
		},
		ParameterizedQueries: quries,
	}

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

func jobnetIconAbort(processData common.IconExecutionProcessData) error {
	jobnetRunData := common.JobnetRunData{
		InnerJobnetId: processData.JobnetRunData.InnerJobnetId,
		JobnetID:      processData.JobnetRunData.JobnetID,
	}

	eventData := common.EventData{
		Event: common.Event{
			Name:      common.EventJobnetIconStop,
			UniqueKey: common.GetUniqueKey(common.IconExecManagerProcess),
		},
		NextProcess: common.NextProcess{
			Name: common.JobnetManagerProcess,
			Data: jobnetRunData,
		},
	}

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

func extIconAbort(processData common.IconExecutionProcessData) error {
	fmt.Println("extIconAbort is called")
	var extData common.IconExtData

	iconDataByte, err := json.Marshal(processData.RunJobData.Data)
	if err != nil {
		return fmt.Errorf("Marshal() failed, err: %s", err.Error())
	}

	err = json.Unmarshal(iconDataByte, &extData)
	if err != nil {
		return fmt.Errorf("Unmarshal() failed, data: %s err: %s", string(iconDataByte), err.Error())
	}

	switch extData.CommandId {
	case common.JA_CMD_TIME, common.JA_CMD_SLEEP:
		err = timerIconAbort(processData)
	case common.JA_CMD_WEEK, common.ZABBIX_SENDER:
		err = killClient(processData.RunJobData.ClientPID) // kill client process
	default:
		err = fmt.Errorf("unknown commandID: %s", extData.CommandId)
	}

	return err
}

func killClient(pid int) error {
	process, err := os.FindProcess(pid)
	if err != nil {
		return err
	}

	err = process.Signal(syscall.Signal(0))
	if err != nil {
		return err
	}

	return process.Kill()
}

func EventIconAbort(eventData common.EventData) error {
	loadCommonVars()
	log := logger.Logging{}
	var err error

	processData, ok := eventData.NextProcess.Data.(common.IconExecutionProcessData)
	if !ok {
		err := fmt.Errorf(
			"type cast failed. expected type: common.IconExecutionProcessData, data: %v",
			eventData.NextProcess.Data)
		log.JaLog("JAEXEC100001", eventData.Event.Name, err.Error(), eventData.NextProcess.Data)
		os.Exit(1)
	}

	// set up log data
	log.InnerJobnetID = processData.RunJobData.InnerJobnetID
	log.InnerJobID = processData.RunJobData.InnerJobID
	log.JobID = processData.RunJobData.JobID

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

	// clean up incoming event data on finish
	defer func() {
		switch processData.RunJobData.IconType {
		case common.IconTypeFWait,
			common.IconTypeReboot,
			common.IconTypeJob:
			// these icons are using clients to abort, no need to clean transcation file.
			// exec_manager will do clean up.
			if processData.RunJobData.Status == common.StatusReady {
				cleanupEventDataDest(eventData, log)
			}
		default:
			cleanupEventDataDest(eventData, log)
		}
	}()

	switch processData.RunJobData.IconType {
	case common.IconTypeLess:
		// kill ssh client
		err = killClient(processData.SessionData.PID)
		if err != nil {
			break
		}

		// give some time to client to exit properly
		time.Sleep(1 * time.Second)

		// kill icon client
		err = killClient(processData.RunJobData.ClientPID) // kill client process
	case common.IconTypeFWait,
		common.IconTypeReboot,
		common.IconTypeJob:
		if processData.RunJobData.Status == common.StatusReady {
			err = killClient(processData.RunJobData.ClientPID)
		} else {
			err = EventIconReady(eventData)
		}
	case common.IconTypeExtJob:
		if processData.RunJobData.Status == common.StatusReady {
			err = killClient(processData.RunJobData.ClientPID)
		} else {
			err = extIconAbort(processData)
		}
	case common.IconTypeJobnet:
		if processData.RunJobData.Status == common.StatusReady {
			err = killClient(processData.RunJobData.ClientPID)
		} else {
			err = jobnetIconAbort(processData)
		}
	case common.IconTypeFCopy,
		common.IconTypeTask,
		common.IconTypeRel,
		common.IconTypeIfEnd,
		common.IconTypeW,
		common.IconTypeValue,
		common.IconTypeCalc,
		common.IconTypeInfo,
		common.IconTypeIf,
		common.IconTypeL,
		common.IconTypeStart,
		common.IconTypeEnd,
		common.IconTypeLink,
		common.IconTypeM:
		err = killClient(processData.RunJobData.ClientPID) // kill client process
	default:
		err = fmt.Errorf("unknown icon type: %d", processData.RunJobData.IconType)
	}

	if err != nil {
		log.JaLog("JAEXECABORT000002", log.InnerJobnetID, log.InnerJobID,
			log.JobnetID, log.JobID, processData.RunJobData.IconType, err.Error())
		
		if processData.RunJobData.Status == common.StatusReady {
			logMessage, _ := log.JaLog("JAEXECABORT000006", log.InnerJobnetID, log.InnerJobID,
				log.JobnetID, log.JobID, processData.RunJobData.IconType, err.Error())
			
			return setRunErr(logMessage, processData)
		}
	}

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

	return nil
}
