/*
** 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 (
	"context"
	"encoding/json"
	"fmt"
	"strconv"
	"sync"
	"time"

	"jobarranger2/src/libs/golibs/common"
	"jobarranger2/src/libs/golibs/database"
	"jobarranger2/src/libs/golibs/event"
	"jobarranger2/src/libs/golibs/logger/logger"
	"jobarranger2/src/libs/golibs/utils"
	wm "jobarranger2/src/libs/golibs/worker_manager"
)

type TimeoutTrigger struct {
	CommandId         string          `json:"command_id"`
	InnerJobnetID     uint64          `json:"inner_jobnet_id"`
	InnerJobnetMainID uint64          `json:"inner_jobnet_main_id"`
	InnerJobID        uint64          `json:"inner_job_id"`
	JobnetID          string          `json:"jobnet_id"`
	JobnetName        string          `json:"jobnet_name"`
	JobnetStatus      int             `json:"status"`
	UpdateDate        uint64          `json:"update_date"`
	JobId             string          `json:"job_id"`
	JobName           string          `json:"job_name"`
	ExecUserName      string          `json:"execution_user_name"`
	CreatedDate       uint64          `json:"created_date"`
	Timeout           uint64          `json:"timeout"`
	IconType          int             `json:"icon_type"`
	RunType           int             `json:"run_type"`
	TimeoutRunType    int             `json:"timeout_run_type"`
	PublicFlag        int             `json:"public_flag"`
	MethodFlag        int             `json:"method_flag"`
	AbortFlag         bool            `json:"abort_flag"`
	EndFlag           bool            `json:"end_flag"`
	BeforeVariable    json.RawMessage `json:"before_variable"`
	SeqNo             int             `json:"seq_no"`
	StartTime         int64           `json:"start_time"`
	RunCount          int             `json:"run_count"`
}

type TimeoutKey struct {
	InnerJobnetID uint64
	InnerJobID    uint64
	CreatedDate   uint64
}

var (
	timeoutProcessID        string = "Timeout Process"
	recordDeletionProcessID string = "Timeout Record Deletion Process"
	timeoutTable            string = "ja_2_run_timeout_table"
	wg                      sync.WaitGroup
	sem                     = make(chan struct{}, 50)        // concurrency limit
	deleteChan              = make(chan TimeoutTrigger, 100) // deletes queue
	// Map to store cancel funcs for running goroutines, protect with mutex
	ctxMap = make(map[TimeoutKey]context.CancelFunc)
	mu     sync.Mutex
)

func ProcessEventData(data common.Data) {
}

func StartDaemonWorkers(data common.Data) {
	wm.StartWorker(func() { handleTimeout(data.DB) }, timeoutProcessID, 180, 0, string(common.TimerManagerProcess))
	wm.StartWorker(func() { recordDeletion(data.DB) }, recordDeletionProcessID, 180, 0, string(common.TimerManagerProcess))
}

func handleTimeout(db database.Database) {
	funcName := "handleTimeout"
	timeoutTable := "ja_2_run_timeout_table"

	logger.JaLog("JATIMER400001", logger.Logging{}, funcName)

	// Get db connection
	dbConn, err := db.GetConn()
	if err != nil {
		logger.JaLog("JATIMER200011", logger.Logging{}, funcName, err.Error())
		return
	}

	for {
		select {
		case <-wm.Wm.Ctx.Done():
			logger.JaLog("JATIMER400007", logger.Logging{}, funcName, timeoutProcessID)
			wg.Wait()
			close(deleteChan)
			return

		case <-time.After(1 * time.Second):
			wm.Wm.MonitorChan <- timeoutProcessID
		}

		timeoutQuery := fmt.Sprintf("SELECT command_id, inner_jobnet_id, inner_job_id, jobnet_id, job_id, execution_user_name, created_date, "+
			"timeout, timeout_run_type, abort_flag, end_flag, icon_type "+
			"FROM %s "+
			"WHERE timeout = (SELECT MIN(timeout) FROM %s) "+
			"UNION "+
			"SELECT command_id, inner_jobnet_id, inner_job_id, jobnet_id, job_id, execution_user_name, created_date, "+
			"timeout, timeout_run_type, abort_flag, end_flag, icon_type "+
			"FROM %s "+
			"WHERE abort_flag = TRUE OR end_flag = TRUE",
			timeoutTable, timeoutTable, timeoutTable)

		// Select Query from ja_2_run_timeout_table for the process timeout
		result, err := dbConn.Select(timeoutQuery)
		if err != nil {
			logger.JaLog("JATIMER200001", logger.Logging{}, funcName, "timeout information", err.Error())
			continue
		}

		for result.HasNextRow() {
			row, err := result.Fetch()
			if err != nil {
				logger.JaLog("JATIMER200002", logger.Logging{}, funcName, "timeout information", err.Error())
				continue
			}

			var tt TimeoutTrigger
			if err := utils.MapStringStringToStruct(row, &tt); err != nil {
				logger.JaLog("JATIMER200003", logger.Logging{}, funcName, "timeout information", err.Error())
				continue
			}
			beforeVarQuery := fmt.Sprintf("SELECT v.seq_no, v.before_variable, r.start_time, r.job_name, r.run_count, r.method_flag "+
				"FROM ja_2_run_job_variable_table v "+
				"JOIN ja_2_run_job_table r "+
				"ON r.inner_job_id = v.inner_job_id AND r.inner_jobnet_id = v.inner_jobnet_id "+
				"WHERE v.inner_job_id = %d AND v.inner_jobnet_id = %d "+
				"ORDER BY v.seq_no DESC "+
				"LIMIT 1",
				tt.InnerJobID, tt.InnerJobnetID)

			beforeVarResult, err := dbConn.Select(beforeVarQuery)
			if err != nil {
				logger.JaLog("JATIMER200001", logger.Logging{}, funcName, "before variable", err.Error())
				continue
			}
			if beforeVarResult.HasNextRow() {
				row, err := beforeVarResult.Fetch()
				if err != nil {
					logger.JaLog("JATIMER200002", logger.Logging{}, funcName, "before variable", err.Error())
					continue
				}

				tt.SeqNo, err = strconv.Atoi(row["seq_no"])
				if err != nil {
					logger.JaLog("JATIMER200012", logger.Logging{}, funcName, "int", "seq_no", err.Error())
					continue
				}

				tt.BeforeVariable = json.RawMessage(row["before_variable"])

				tt.StartTime, err = strconv.ParseInt(row["start_time"], 10, 64)
				if err != nil {
					logger.JaLog("JATIMER200012", logger.Logging{}, funcName, "uint64", "start_time", err.Error())
					continue
				}

				tt.JobName = row["job_name"]

				tt.RunCount, err = strconv.Atoi(row["run_count"])
				if err != nil {
					logger.JaLog("JATIMER200012", logger.Logging{}, funcName, "int", "run_count", err.Error())
					continue
				}

				tt.MethodFlag, err = strconv.Atoi(row["method_flag"])
				if err != nil {
					logger.JaLog("JATIMER200012", logger.Logging{}, funcName, "int", "method_flag", err.Error())
					continue
				}
			}

			jobnetQuery := fmt.Sprintf("SELECT inner_jobnet_main_id, status, update_date, run_type, public_flag, "+
				"jobnet_name FROM ja_2_run_jobnet_table WHERE inner_jobnet_id = %d",
				tt.InnerJobnetID,
			)

			jobnetResult, err := dbConn.Select(jobnetQuery)
			if err != nil {
				logger.JaLog("JATIMER200001", logger.Logging{}, funcName, "jobnet information", err.Error())
				continue
			}

			if jobnetResult.HasNextRow() {
				row, err := jobnetResult.Fetch()
				if err != nil {
					logger.JaLog("JATIMER200002", logger.Logging{}, funcName, "jobnet information", err.Error())
					continue
				}

				tt.InnerJobnetMainID, err = strconv.ParseUint(row["inner_jobnet_main_id"], 10, 64)
				if err != nil {
					logger.JaLog("JATIMER200012", logger.Logging{}, funcName, "uint64", "inner_jobnet_main_id", err.Error())
					continue
				}

				tt.JobnetStatus, err = strconv.Atoi(row["status"])
				if err != nil {
					logger.JaLog("JATIMER200012", logger.Logging{}, funcName, "int", "jobnet status", err.Error())
					continue
				}

				tt.UpdateDate, err = strconv.ParseUint(row["update_date"], 10, 64)
				if err != nil {
					logger.JaLog("JATIMER200012", logger.Logging{}, funcName, "uint64", "update_date", err.Error())
					continue
				}

				tt.RunType, err = strconv.Atoi(row["run_type"])
				if err != nil {
					logger.JaLog("JATIMER200012", logger.Logging{}, funcName, "int", "run_type", err.Error())
					continue
				}

				tt.PublicFlag, err = strconv.Atoi(row["public_flag"])
				if err != nil {
					logger.JaLog("JATIMER200012", logger.Logging{}, funcName, "int", "public_flag", err.Error())
					continue
				}

				tt.JobnetName = row["jobnet_name"]
			}

			key := TimeoutKey{tt.InnerJobnetID, tt.InnerJobID, tt.CreatedDate}

			// ----------------------------------------------------------------------------------------
			// If abort_flag OR end_flag is TRUE AND goroutine does NOT exist → send abort event
			// ----------------------------------------------------------------------------------------
			if tt.AbortFlag || tt.EndFlag {
				mu.Lock()
				cancelFunc, exists := ctxMap[key]
				mu.Unlock()

				if exists {
					// Cancel the running goroutine
					cancelFunc()
				} else {
					// No goroutine exists → must send abort event immediately
					if tt.IconType == int(common.IconTypeExtJob) {
						if err := processExtIconTimeoutAbort(&tt); err != nil {
							logger.JaLog("JATIMER200009", logger.Logging{
								InnerJobnetID: tt.InnerJobnetID,
								InnerJobID:    tt.InnerJobID,
								JobID:         tt.JobId,
							}, funcName, tt.InnerJobnetID, tt.InnerJobID, tt.JobId, err.Error())
						}
					}
					// Send to delete queue
					deleteChan <- tt
				}
				continue
			}

			mu.Lock()
			// Skip if goroutine already running
			if _, running := ctxMap[key]; running {
				mu.Unlock()
				continue
			}

			// Create new goroutine context
			ctx, cancel := context.WithCancel(context.Background())
			ctxMap[key] = cancel
			mu.Unlock()

			sem <- struct{}{}
			wg.Add(1)

			go func(ctx context.Context, tt TimeoutTrigger, key TimeoutKey) {
				defer wg.Done()
				defer func() {
					<-sem
					mu.Lock()
					delete(ctxMap, key)
					mu.Unlock()
				}()

				waitDuration := time.Until(time.Unix(int64(tt.Timeout), 0))
				select {
				case <-ctx.Done():
					// Aborted early, create next event if ext icon
					if tt.IconType == int(common.IconTypeExtJob) {
						if err := processExtIconTimeoutAbort(&tt); err != nil {
							logger.JaLog("JATIMER200009", logger.Logging{
								InnerJobnetID: tt.InnerJobnetID,
								InnerJobID:    tt.InnerJobID,
								JobID:         tt.JobId,
							}, funcName, tt.InnerJobnetID, tt.InnerJobID, tt.JobId, err.Error())
						}
					}
				case <-time.After(waitDuration):
					if err := processTimeout(&tt); err != nil {
						logger.JaLog("JATIMER200004", logger.Logging{
							InnerJobnetID: tt.InnerJobnetID,
							InnerJobID:    tt.InnerJobID,
							JobID:         tt.JobId,
						}, funcName, tt.JobId, err.Error())
					}
				}

				deleteChan <- tt
			}(ctx, tt, key)
			beforeVarResult.Free()
			jobnetResult.Free()
		}

		result.Free()
		time.Sleep(1 * time.Second)
	}
}

func recordDeletion(db database.Database) {
	funcName := "recordDeletion"

	logger.JaLog("JATIMER400006", logger.Logging{}, funcName)

	// Get db connection
	dbConn, err := db.GetConn()
	if err != nil {
		logger.JaLog("JATIMER200011", logger.Logging{}, funcName, err.Error())
		return
	}

	ticker := time.NewTicker(1 * time.Second)
	defer ticker.Stop()

	for {
		select {
		case tt, ok := <-deleteChan:
			if !ok {
				logger.JaLog("JATIMER400008", logger.Logging{}, funcName)
				return
			}
			err := dbConn.Begin()
			if err != nil {
				logger.JaLog("JATIMER200005", logger.Logging{
					InnerJobnetID: tt.InnerJobnetID,
					InnerJobID:    tt.InnerJobID,
					JobID:         tt.JobId,
				}, funcName, tt.InnerJobnetID, tt.InnerJobID, tt.JobId, err.Error())
				continue
			}
			deleteQuery := fmt.Sprintf("DELETE FROM %s WHERE inner_jobnet_id = ? AND inner_job_id = ? AND created_date = ?",
				timeoutTable)

			_, err = dbConn.Execute(deleteQuery, tt.InnerJobnetID, tt.InnerJobID, tt.CreatedDate)
			if err != nil {
				logger.JaLog("JATIMER200007", logger.Logging{
					InnerJobnetID: tt.InnerJobnetID,
					InnerJobID:    tt.InnerJobID,
					JobID:         tt.JobId,
				}, funcName, tt.InnerJobnetID, tt.InnerJobID, tt.JobId, err.Error())
				err = dbConn.Rollback()
				if err != nil {
					logger.JaLog("JATIMER200010", logger.Logging{
						InnerJobnetID: tt.InnerJobnetID,
						InnerJobID:    tt.InnerJobID,
						JobID:         tt.JobId,
					}, funcName, tt.InnerJobnetID, tt.InnerJobID, tt.JobId, err.Error())
				}
				continue
			} else {
				logger.JaLog("JATIMER400002", logger.Logging{
					InnerJobnetID: tt.InnerJobnetID,
					InnerJobID:    tt.InnerJobID,
					JobID:         tt.JobId,
				}, funcName, tt.InnerJobnetID, tt.InnerJobID, tt.JobId)
			}
			err = dbConn.Commit()
			if err != nil {
				logger.JaLog("JATIMER200006", logger.Logging{
					InnerJobnetID: tt.InnerJobnetID,
					InnerJobID:    tt.InnerJobID,
					JobID:         tt.JobId,
				}, funcName, tt.InnerJobnetID, tt.InnerJobID, tt.JobId, err.Error())
				err = dbConn.Rollback()
				if err != nil {
					logger.JaLog("JATIMER200010", logger.Logging{
						InnerJobnetID: tt.InnerJobnetID,
						InnerJobID:    tt.InnerJobID,
						JobID:         tt.JobId,
					}, funcName, tt.InnerJobnetID, tt.InnerJobID, tt.JobId, err.Error())
				}
				continue
			}
			// Send heartbeat after processing deletion
			wm.Wm.MonitorChan <- recordDeletionProcessID

		case <-ticker.C:
			// Heartbeat even if no deletion event
			wm.Wm.MonitorChan <- recordDeletionProcessID
		}
	}
}

func processTimeout(tt *TimeoutTrigger) error {
	funcName := "processTimeout"
	now := uint64(time.Now().Unix())
	logger.JaLog("JATIMER400003", logger.Logging{
		InnerJobnetID: tt.InnerJobnetID,
		InnerJobID:    tt.InnerJobID,
		JobID:         tt.JobId,
	}, funcName, tt.InnerJobnetID, tt.InnerJobID, tt.JobId)

	if !(tt.Timeout <= now) {
		return nil
	}

	if !(tt.InnerJobnetID != 0) {
		return nil
	}

	if tt.InnerJobID == 0 {
		// JOBNET TIMEOUT
		err := prepareEventData(common.EventJobnetTimeout, *tt, common.JobnetManagerProcess, common.JobnetRunData{
			InnerJobnetId:  tt.InnerJobnetID,
			TimeoutRunType: tt.TimeoutRunType,
			JobnetID:       tt.JobnetID,
		})
		if err != nil {
			logger.JaLog("JATIMER200008", logger.Logging{
				InnerJobnetID: tt.InnerJobnetID,
				InnerJobID:    tt.InnerJobID,
				JobID:         tt.JobId,
			}, funcName, tt.InnerJobnetID, tt.InnerJobID, tt.JobId, err.Error())
			return err
		}
		return nil
	}

	// ICON TIMEOUT
	if tt.IconType == int(common.IconTypeExtJob) {
		extData := common.IconExtData{
			CommandId: tt.CommandId,
		}

		dataBytes, err := json.Marshal(extData)
		if err != nil {
			return err
		}

		var extMap map[string]any
		err = json.Unmarshal(dataBytes, &extMap)
		if err != nil {
			return err
		}

		err = prepareEventData(common.EventIconResultUpdate, *tt, common.IconResultManagerProcess, common.IconExecutionProcessData{
			JobResult: common.JobResultData{
				JobRunRequestData: common.JobRunRequestData{
					JobID: strconv.FormatUint(tt.InnerJobID, 10),
				},
				Result: common.JA_JOBRESULT_SUCCEED,
			},
			RunJobData: common.RunJobTable{
				InnerJobnetID: tt.InnerJobnetID,
				InnerJobID:    tt.InnerJobID,
				IconType:      common.IconType(tt.IconType),
				Data:          extMap,
				JobID:         tt.JobId,
				JobName:       tt.JobName,
				StartTime:     tt.StartTime,
				RunCount:      tt.RunCount,
				MethodFlag:    common.JobRunMethod(tt.MethodFlag),
			},
			RunJobVariableData: common.RunJobVariableTable{
				BeforeVariable: tt.BeforeVariable,
				SeqNo:          tt.SeqNo,
				InnerJobnetID:  tt.InnerJobnetID,
			},
			RunJobnetData: common.RunJobnetTable{
				InnerJobnetID:     tt.InnerJobnetID,
				InnerJobnetMainID: tt.InnerJobnetMainID,
				JobnetID:          tt.JobnetID,
				JobnetName:        tt.JobnetName,
				Status:            common.StatusType(tt.JobnetStatus),
				UpdateDate:        tt.UpdateDate,
				RunType:           tt.RunType,
				PublicFlag:        tt.PublicFlag,
				ExecutionUserName: tt.ExecUserName,
			},
		})
		if err != nil {
			logger.JaLog("JATIMER200008", logger.Logging{
				InnerJobnetID: tt.InnerJobnetID,
				InnerJobID:    tt.InnerJobID,
				JobID:         tt.JobId,
			}, funcName, tt.InnerJobnetID, tt.InnerJobID, tt.JobId, err.Error())
			return err
		}
	} else {
		iconTimeout := common.IconTimeoutData{
			TimeoutRunType: tt.TimeoutRunType,
			Timeout:        tt.Timeout,
		}

		dataBytes, err := json.Marshal(iconTimeout)
		if err != nil {
			return err
		}

		var iconTimeoutMap map[string]any
		err = json.Unmarshal(dataBytes, &iconTimeoutMap)
		if err != nil {
			return err
		}

		err = prepareEventData(common.EventIconTimeout, *tt, common.IconResultManagerProcess, common.IconExecutionProcessData{
			JobResult: common.JobResultData{
				JobRunRequestData: common.JobRunRequestData{
					JobID: strconv.FormatUint(tt.InnerJobID, 10),
				},
			},
			RunJobData: common.RunJobTable{
				InnerJobID:    tt.InnerJobID,
				InnerJobnetID: tt.InnerJobnetID,
				IconType:      common.IconType(tt.IconType),
				Data:          iconTimeoutMap,
				JobID:         tt.JobId,
				JobName:       tt.JobName,
				StartTime:     tt.StartTime,
				RunCount:      tt.RunCount,
				MethodFlag:    common.JobRunMethod(tt.MethodFlag),
			},
			RunJobVariableData: common.RunJobVariableTable{
				BeforeVariable: tt.BeforeVariable,
				SeqNo:          tt.SeqNo,
				InnerJobnetID:  tt.InnerJobnetID,
			},
			RunJobnetData: common.RunJobnetTable{
				InnerJobnetID:     tt.InnerJobnetID,
				InnerJobnetMainID: tt.InnerJobnetMainID,
				JobnetID:          tt.JobnetID,
				JobnetName:        tt.JobnetName,
				Status:            common.StatusType(tt.JobnetStatus),
				UpdateDate:        tt.UpdateDate,
				RunType:           tt.RunType,
				PublicFlag:        tt.PublicFlag,
				ExecutionUserName: tt.ExecUserName,
			},
		})

		if err != nil {
			logger.JaLog("JATIMER200008", logger.Logging{
				InnerJobnetID: tt.InnerJobnetID,
				InnerJobID:    tt.InnerJobID,
				JobID:         tt.JobId,
			}, funcName, tt.InnerJobnetID, tt.InnerJobID, tt.JobId, err.Error())
			return err
		}
	}

	return nil
}

func processExtIconTimeoutAbort(tt *TimeoutTrigger) error {
	funcName := "processTimeoutAbort"
	logger.JaLog("JATIMER400004", logger.Logging{
		InnerJobnetID: tt.InnerJobnetID,
		InnerJobID:    tt.InnerJobID,
		JobID:         tt.JobId,
	}, funcName, tt.InnerJobnetID, tt.InnerJobID, tt.JobId)

	extData := common.IconExtData{
		CommandId: tt.CommandId,
	}

	dataBytes, err := json.Marshal(extData)
	if err != nil {
		return err
	}

	var extMap map[string]any
	err = json.Unmarshal(dataBytes, &extMap)
	if err != nil {
		return err
	}

	err = prepareEventData(common.EventIconResultUpdate, *tt, common.IconResultManagerProcess, common.IconExecutionProcessData{
		JobResult: common.JobResultData{
			JobRunRequestData: common.JobRunRequestData{
				JobID: strconv.FormatUint(tt.InnerJobID, 10),
			},
			Message:    "Aborted the job.",
			Result:     common.JA_JOBRESULT_FAIL,
			ReturnCode: 1,
		},
		RunJobData: common.RunJobTable{
			InnerJobnetID: tt.InnerJobnetID,
			InnerJobID:    tt.InnerJobID,
			IconType:      common.IconType(tt.IconType),
			Data:          extMap,
			JobID:         tt.JobId,
			JobName:       tt.JobName,
			StartTime:     tt.StartTime,
			RunCount:      tt.RunCount,
			MethodFlag:    common.JobRunMethod(tt.MethodFlag),
		},
		RunJobVariableData: common.RunJobVariableTable{
			BeforeVariable: tt.BeforeVariable,
			SeqNo:          tt.SeqNo,
			InnerJobnetID:  tt.InnerJobnetID,
		},
		RunJobnetData: common.RunJobnetTable{
			InnerJobnetID:     tt.InnerJobnetID,
			InnerJobnetMainID: tt.InnerJobnetMainID,
			JobnetID:          tt.JobnetID,
			JobnetName:        tt.JobnetName,
			Status:            common.StatusType(tt.JobnetStatus),
			UpdateDate:        tt.UpdateDate,
			RunType:           tt.RunType,
			PublicFlag:        tt.PublicFlag,
			ExecutionUserName: tt.ExecUserName,
		},
	})
	if err != nil {
		logger.JaLog("JATIMER200008", logger.Logging{
			InnerJobnetID: tt.InnerJobnetID,
			InnerJobID:    tt.InnerJobID,
			JobID:         tt.JobId,
		}, funcName, tt.InnerJobnetID, tt.InnerJobID, tt.JobId, err.Error())
		return err
	}
	return nil
}

func prepareEventData[T any](eventName common.EventName, tt TimeoutTrigger, procType common.ProcessType, nextProcData T) error {
	funcName := "prepareEventData"
	logger.JaLog("JATIMER400005", logger.Logging{
		InnerJobnetID: tt.InnerJobnetID,
		InnerJobID:    tt.InnerJobID,
		JobID:         tt.JobId,
	}, funcName, tt.InnerJobnetID, tt.InnerJobID, tt.JobId)
	var eventData common.EventData
	eventData.Event.Name = eventName
	eventData.Event.UniqueKey = common.GetUniqueKey(common.TimerManagerProcess)
	eventData.NextProcess.Name = procType
	eventData.NextProcess.Data = nextProcData

	err := event.CreateNextEvent(eventData, tt.InnerJobnetID, tt.JobnetID, tt.InnerJobID)
	if err != nil {
		return err
	}
	return nil
}
