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

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

func processEnd(jobnetPayload common.EventData, dbconn database.DBConnection) error {
	funcname := "processEnd"
	JobnetRunLogger.JaLog("JAJOBNETRUN400001", funcname)

	queries := []common.ParameterizedQuery{}

	jobnetRunData, err := getJobnetRunData(jobnetPayload.NextProcess.Data)
	if err != nil {
		JobnetRunLogger.JaLog("JAJOBNETRUN200008", err.Error())
		return err
	}

	// Info: show the jobnet run identifiers we're processing
	JobnetRunLogger.JaLog("JAJOBNETRUN400012", jobnetRunData.InnerJobnetId, jobnetRunData.JobnetID, jobnetRunData.InnerJobId)

	isSubJobnet, innerJobnetMainId, err := IsSubJobnet(dbconn, jobnetRunData.InnerJobnetId)
	if err != nil {
		return fmt.Errorf("%s", err.Error())
	}

	// DEBUG: record mapping and whether this is a sub-jobnet
	JobnetRunLogger.JaLog("JAJOBNETRUN400013", innerJobnetMainId, isSubJobnet)

	JobnetRunLogger.InnerJobnetMainID = innerJobnetMainId

	// status with running,ready and method flag is not abort.
	runningJobIdList, err := utils.GetRunningJobIDList(dbconn, jobnetRunData.InnerJobnetId)
	if err != nil {
		return fmt.Errorf("%s", err.Error())
	}
	// DEBUG: list running jobs and count
	JobnetRunLogger.JaLog("JAJOBNETRUN400014", runningJobIdList, len(runningJobIdList))
	hasRunningJobs := len(runningJobIdList) > 0

	endStatus := common.StatusEnd
	var nextEvent common.EventData

	if jobnetPayload.Event.Name == common.EventJobnetStop || jobnetPayload.Event.Name == common.EventJobnetIconStop {
		// stop the running go routine
		GetScheduleJobnets().Remove(jobnetRunData.InnerJobnetId)
		JobnetRunLogger.JobnetStatus = common.StatusRunErr
		if jobnetRunData.IconStopInterval == 0 {
			JobnetRunLogger.JaJobLog(common.JC_JOBNET_ERR_END)
		}
		endStatus = common.StatusEndErr
		if jobnetPayload.Event.Name == common.EventJobnetIconStop {
			queries = jaSetJobStatusSummary(jobnetRunData.InnerJobnetId, JA_SUMMARY_JOB_STATUS_ERROR, queries...)
		}
		queries = jaSetAbortFlagSummary(jobnetRunData.InnerJobnetId, queries...)
		queries = jaSetAbortFlagTimeout(jobnetRunData.InnerJobnetId, queries...)
	} else {
		JobnetRunLogger.JobnetStatus = common.StatusEnd

		timeoutFlag, err := utils.GetTimeoutFlag(dbconn, jobnetRunData.InnerJobnetId)
		if err != nil {
			return err
		}

		// WARN: timeout flag detected
		if timeoutFlag == 1 {
			JobnetRunLogger.JaLog("JAJOBNTRUN300002", jobnetRunData.InnerJobnetId)
		} else {
			queries = jaSetJobStatusSummary(jobnetRunData.InnerJobnetId, JA_SUMMARY_JOB_STATUS_NORMAL, queries...)
		}

		JobnetRunLogger.JobnetStatus = common.StatusRun
		JobnetRunLogger.JaJobLog(common.JC_JOBNET_END)
		queries = jaSetEndFlagTimeout(jobnetRunData.InnerJobnetId, queries...)
	}

	switch {
	case !hasRunningJobs:
		// if no running job update the jobnet status as END
		queries, err = updateJobnetStatus(jobnetRunData, isSubJobnet, endStatus, queries)
		if err != nil {
			return err
		}

		// DEBUG: show queries prepared so far
		JobnetRunLogger.JaLog("JAJOBNETRUN400014", queries, len(queries))

		if isSubJobnet {
			// prepare EventSubJobnetEnd
			nextEvent = prepareNextEventData(
				common.EventSubJobnetStatusChange,
				common.FlowManagerProcess,
				common.FlowProcessData{
					InnerJobnetId: jobnetRunData.InnerJobnetId,
					Status:        endStatus,
					JobnetId:      jobnetRunData.JobnetID,
				},
				queries,
			)
		} else {
			// prepare EventJobnetEnd
			nextEvent = prepareNextEventData(
				common.EventJobnetEnd,
				common.DBSyncerManagerProcess,
				map[string]any{},
				queries,
			)
			sqlCond := builder.NewSQLCondition(
				fmt.Sprintf("select status from ja_2_run_jobnet_table where inner_jobnet_id = %d", jobnetRunData.InnerJobnetId),
			).
				AddCondition(
					builder.NewCondition("ignore").
						Field("status", common.OpEq, common.StatusEnd).
						Build(),
				).
				DefaultAction(common.ActionExec). // move to error folder
				Build()
			nextEvent.SQLConditions = []common.SQLCondition{sqlCond}
		}

	case hasRunningJobs:
		if isSubJobnet {
			// Add Flag here
			JobnetRunLogger.JaLog("JAJOBNETRUN400018", jobnetRunData.InnerJobnetId)
			queries = jaSetEndingFlagJobnet(jobnetRunData.InnerJobnetId, 1, queries...)
		} else {
			JobnetRunLogger.JaLog("JAJOBNETRUN400019", jobnetRunData.InnerJobnetId)
			queries = jaSetEndingFlagSummary(jobnetRunData.InnerJobnetId, 1, queries...)
			queries = jaSetEndingFlagJobnet(jobnetRunData.InnerJobnetId, 1, queries...)
		}
		if jobnetRunData.IconStopInterval == 0 {
			nextEvent = prepareNextEventData(
				common.EventJobnetEnding,
				common.DBSyncerManagerProcess,
				map[string]any{},
				queries,
			)
		}
	}

	// Use debug log message mapping for creating next event
	JobnetRunLogger.JaLog("JAJOBNETRUN400009", nextEvent)

	if nextEvent.Event.Name != "" {
		err = event.CreateNextEvent(nextEvent, jobnetRunData.InnerJobnetId, jobnetRunData.JobnetID, jobnetRunData.InnerJobId)
		if err != nil {
			return fmt.Errorf("%s", err.Error())
		}
	}

	interval := server.Options.IconStopInterval
	if hasRunningJobs {
		// DEBUG: waiting loop start
		JobnetRunLogger.JaLog("JAJOBNETRUN400015", jobnetRunData.InnerJobnetId, interval, endStatus)
		currentFilePath := jobnetPayload.Transfer.Files[0].Source
		jobsFinished, err := waitForJobsAndComplete(dbconn, jobnetRunData, isSubJobnet, uint64(interval), endStatus, currentFilePath)
		if err != nil {
			return fmt.Errorf("%s", err.Error())
		}
		if !jobsFinished {
			return nil
		}
		JobnetRunLogger.JaLog("JAJOBNETRUN000006", jobnetRunData.InnerJobnetId)
	}

	// first move the incoming file from flow manager
	sourceFileName := jobnetPayload.Transfer.Files[0].Source

	// DEBUG: log current path and move operation
	JobnetRunLogger.JaLog("JAJOBNETRUN400006", sourceFileName)
	JobnetRunLogger.JaLog("JAJOBNETRUN400005", "in", "end")

	err = utils.MoveToSubFolder(sourceFileName, END)
	if err != nil {
		return fmt.Errorf("%s", err.Error())
	}
	JobnetRunLogger.JaLog("JAJOBNETRUN000007", sourceFileName, "in", "end")

	if err := moveFilesToEndForInnerJobnet(jobnetRunData.InnerJobnetId); err != nil {
		return err
	}
	JobnetRunLogger.JaLog("JAJOBNETRUN400020")

	waitingFile, err := getSortedRunningJobnetFile(jobnetRunData.JobnetID, JOBNETMANAGER_WAIT_DIR)
	if err != nil {
		return fmt.Errorf("%s", err.Error())
	}

	if waitingFile != "" {
		JobnetRunLogger.JaLog("JAJOBNETRUN400017", waitingFile)
		innerJobnetIdFromFile, err := getInnerJobnetIDFromFilename(waitingFile)
		if err != nil {
			return fmt.Errorf("%s", err.Error())
		}
		waitingFile = filepath.Join(JOBNETMANAGER_WAIT_DIR, waitingFile)

		if !isNotScheduleJobnetId(innerJobnetIdFromFile) {
			sockFilePathFull := filepath.Join(server.Options.UnixSockParentDir, common.JobnetManagerSockFile)
			err = updateTransactionFile(
				jobnetRunData.InnerJobnetId,
				jobnetRunData.InnerJobId,
				string(common.JobnetManagerProcess),
				sockFilePathFull,
				waitingFile,
				func(data *common.JobnetRunData) {
					data.ScheduledTime = 0
				},
			)
		}

		if err != nil {
			return fmt.Errorf("%s", err.Error())
		}

		movingFolder := IN
		if jobnetPayload.Event.Name == common.EventJobnetStop {
			movingFolder = END
		}
		err = utils.MoveToSubFolder(waitingFile, movingFolder)
		if err != nil {
			return fmt.Errorf("%s", err.Error())
		}
		JobnetRunLogger.JaLog("JAJOBNETRUN000007", waitingFile, "wait", movingFolder)
	}

	JobnetRunLogger.JaLog("JAJOBNETRUN400002", funcname)
	return nil
}
