/*
** 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"
	"os"
	"path/filepath"

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

func createMiconAbortFlagFile(innerJobId uint64) error {
	flowManagerEndFolder := filepath.Join(common.ServerTmpFolderPath, common.FlowManagerFolder, common.EndFolder)

	// Build file: <innerJobId>.abort
	abortFile := filepath.Join(
		flowManagerEndFolder,
		fmt.Sprintf("%d.abort", innerJobId),
	)

	// Create abort flag file
	file, err := os.Create(abortFile)
	if err != nil {
		return fmt.Errorf("failed to create multiple start icon abort flag file: %w", err)
	}
	defer file.Close()

	return nil
}

func DeleteMiconAbortFlagFile(innerJobId uint64) error {
	flowManagerEndFolder := filepath.Join(common.ServerTmpFolderPath, common.FlowManagerFolder, common.EndFolder)

	// Build file: <innerJobId>.abort
	abortFile := filepath.Join(
		flowManagerEndFolder,
		fmt.Sprintf("%d.abort", innerJobId),
	)

	// Delete the file
	if err := os.Remove(abortFile); err != nil {
		if os.IsNotExist(err) {
			return nil // already deleted, not an error
		}
		return fmt.Errorf("failed to delete abort flag file %s: %w", abortFile, err)
	}

	return nil
}

func MiconAbortFlagExists(innerJobId uint64) bool {
	flowManagerEndFolder := filepath.Join(common.ServerTmpFolderPath, common.FlowManagerFolder, common.EndFolder)

	// Build file path: <innerJobId>.abort
	abortFile := filepath.Join(
		flowManagerEndFolder,
		fmt.Sprintf("%d.abort", innerJobId),
	)

	_, err := os.Stat(abortFile)
	if err == nil {
		// File exists
		return true
	}

	if os.IsNotExist(err) {
		// File does not exist
		return false
	}

	// Any other error (permission, IO error, etc.)
	// Treat as not aborted, or log if needed
	return false
}

func ProcessIconAbort(conn database.DBConnection, processData common.FlowProcessData) error {
	var nextEventData common.EventData
	var nextProcessData common.IconExecutionProcessData
	var err error
	var funcName = "ProcessIconAbort"
	var processType = common.FlowProcessAbort

	logger.JaLog("JAFLOW400001", logger.Logging{}, funcName,
		fmt.Sprintf("inner_jobnet_id: %d, inner_job_id: %d",
			processData.InnerJobnetId, processData.InnerJobId))

	nextEventData.Event.Name = common.EventIconAbort
	nextEventData.Event.UniqueKey = common.GetUniqueKey(processName)
	nextEventData.NextProcess.Name = common.IconExecManagerProcess

	nextProcessData.RunJobData.InnerJobID = processData.InnerJobId
	_, err = PrepareDataForNextProcess(conn, &nextProcessData, processData, &processType)
	if err != nil {
		err = fmt.Errorf("data preparation for innder_job_id: %d has failed. error: [%v]",
			processData.InnerJobId, err)
		logger.JaLog("JAFLOW200000", logger.Logging{}, funcName, "PrepareDataForNextProcess", err)
		return fmt.Errorf("PrepareDataForNextProcess() failed")
	}

	switch nextProcessData.RunJobData.Status {
	case common.StatusEnd, common.StatusEndErr:
		// ignore icon force stop process
		return nil
	case common.StatusReady:
		lockPQ, updatePQ := builder.GetRunJobMethodFlagChangeQuery(
			processData.InnerJobId, common.MethodAbort)
		//job method_flag change query
		nextEventData.AppendPQs(lockPQ, updatePQ)

		sqlCond := BuildSQlConditionForReadyIconUserActionForceStop(nextProcessData.RunJobData.InnerJobID)
		nextEventData.SQLConditions = []common.SQLCondition{sqlCond}
	default:
		if nextProcessData.RunJobData.Status == common.StatusRunErr &&
			nextProcessData.RunJobData.IconType != common.IconTypeJobnet {
			return nil
		}

		lockPQ, updatePQ := builder.GetJobStatusChangeQuery(
			processData.InnerJobId, common.StatusAbort, common.MethodAbort,
			nextProcessData.RunJobData.RunCount, -1, 0)
		//job status and method_flag change query
		nextEventData.AppendPQs(lockPQ, updatePQ)

		switch nextProcessData.RunJobData.Status {
		case common.StatusRun:
			sqlCond := BuildSQlConditionForRunningIconUserActionForceStop(nextProcessData.RunJobData.InnerJobID)
			nextEventData.SQLConditions = []common.SQLCondition{sqlCond}
		case common.StatusAbort:
			sqlCond := BuildSQlConditionForAbortingIconUserActionForceStop(nextProcessData.RunJobData.InnerJobID)
			nextEventData.SQLConditions = []common.SQLCondition{sqlCond}
		}
	}

	switch nextProcessData.RunJobData.IconType {
	case common.IconTypeJob, common.IconTypeFCopy,
		common.IconTypeFWait, common.IconTypeLess:
		if value, ok := nextProcessData.RunJobData.Data["timeout"].(float64); ok && value > 0 {
			lockPQ, updatePQ := builder.GetRunTimeoutTableUpdateAbortFlagQuery(processData.InnerJobId, true)
			nextEventData.AppendPQs(lockPQ, updatePQ)
		}
	case common.IconTypeM:
		if err := createMiconAbortFlagFile(processData.InnerJobId); err != nil {
			return err
		}
		return nil
	}

	nextProcessData.RunJobData.MethodFlag = common.MethodAbort
	nextEventData.NextProcess.Data = nextProcessData

	err = event.CreateNextEvent(nextEventData, nextProcessData.RunJobData.InnerJobnetID, processData.JobnetId, processData.InnerJobId)
	if err != nil {
		logger.JaLog("JAFLOW200000", logger.Logging{}, funcName, "CreateNextEvent", err)
		return fmt.Errorf("CreateNextEvent() failed")
	}
	logger.JaLog("JAFLOW400002", logger.Logging{}, funcName,
		fmt.Sprintf("inner_jobnet_id: %d, inner_job_id: %d",
			nextProcessData.RunJobData.InnerJobnetID, nextProcessData.RunJobData.InnerJobID))
	return nil
}
