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

import (
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"time"

	"jobarranger2/src/libs/golibs/database"
)

var FilePermission os.FileMode = 0755

var (
	ErrIndexOutOfBound = errors.New("index out of bound")
)

// Status codes
const (
	Succeed = true
	Fail    = false
)

const (
	RunCountFolder    = "run_count"
	HostLockSubFolder = "hostlock"
)

var (
	JobargVersion  = "1.0.0"
	JobargRevision = "dev"
	JobargRevDate  = "unknown"
)

const (
	FileLockRetryCount = 30
)

const (
	UdsRoute     = 1
	InotifyRoute = 2
	TcpRoute     = 3
)

const (
	JazVer1 = 1
	JazVer2 = 2
)

const (
	// Agent
	JaAgentPort = "{$JOBARRANGER_AGENT_PORT}"

	// SSH
	JaSSHPort = "{$JOBARRANGER_SSH_PORT}"
	SSHPort   = 22
)

const (
	ClientDataExt      = ".data"
	ClientPIDExt       = ".clientPID"
	ClientReturnExt    = ".ret"
	ClientStdoutExt    = ".out"
	ClientStderrExt    = ".err"
	ClientHelperPIDExt = ".helperPID"
)

var ClientExts = []string{
	ClientDataExt,
	ClientPIDExt,
	ClientReturnExt,
	ClientStdoutExt,
	ClientStderrExt,
	ClientHelperPIDExt,
}

// Store info of running manager
type ManagerInfo struct {
	Name          ProcessType
	PluginPath    string
	FolderPath    string
	SubFolders    []string
	WatchFolders  []string
	SockFilePath  string
	DBMaxConCount int
	DBDaemonFlag  bool
	DBOneTimeFlag bool
}

// Manager Info
var Manager ManagerInfo

const (
	InFolder         = "in"
	RunFolder        = "run"
	WaitFolder       = "wait"
	PendingFolder    = "pending"
	ClientDataFolder = "client_data"
	EndFolder        = "end"
	ReRunFolder      = "rerun"
)

// ProcessType defines types of managers/processes
type ProcessType string

const (
	JobnetManagerProcess       ProcessType = "jobnetmanager"
	FlowManagerProcess         ProcessType = "flowmanager"
	IconExecManagerProcess     ProcessType = "iconexecmanager"
	TrapperManagerProcess      ProcessType = "trappermanager"
	IconResultManagerProcess   ProcessType = "iconresultmanager"
	ZabbixLinkManagerProcess   ProcessType = "zabbixlinkmanager"
	TimerManagerProcess        ProcessType = "timermanager"
	HousekeeperManagerProcess  ProcessType = "housekeepermanager"
	NotificationManagerProcess ProcessType = "notificationmanager"
	DBSyncerManagerProcess     ProcessType = "dbsyncermanager"
	RecoveryManagerProcess     ProcessType = "recoverymanager"
	AgentManagerProcess        ProcessType = "agentmanager"
	JobRunClientProcess        ProcessType = "job_run_client"
	LoggingProcess             ProcessType = "logging"
)

// Managers parent folder name
const (
	JobnetManagerFolder       string = "jobnetmanager"
	FlowManagerFolder         string = "flowmanager"
	IconExecManagerFolder     string = "iconexecmanager"
	TrapperManagerFolder      string = "trappermanager"
	IconResultManagerFolder   string = "iconresultmanager"
	ZabbixLinkManagerFolder   string = "zabbixlinkmanager"
	TimerManagerFolder        string = "timermanager"
	HousekeeperManagerFolder  string = "housekeepermanager"
	NotificationManagerFolder string = "notificationmanager"
	DBSyncerManagerFolder     string = "dbsyncermanager"
	RecoveryManagerFolder     string = "recoverymanager"
	AgentManagerFolder        string = "agentmanager"
)

// Managers sub folders
var (
	JobnetManagerSubFolders       = []string{"in", "run", "wait", "end", "error"}
	FlowManagerSubFolders         = []string{"in", "wait", "end", "error", HostLockSubFolder}
	IconExecManagerSubFolders     = []string{"in", "run", "end", "error", "client_data", RunCountFolder}
	IconResultManagerSubFolders   = []string{"in", "pending", "end", "error"}
	NotificationManagerSubFolders = []string{"in", "end", "error"}
	DBSyncerManagerSubFolders     = []string{"in", "end", "error"}
	AgentManagerSubFolders        = []string{"temp", "in", "data", "exec", "error", "end", "close", "serverIPs", "jobs", "abort"}
	TrapperManagerSubFolders      = []string{"job"}
	RecoveryManagerSubFolders     = []string{ReRunFolder}
)

// Managers watch folders
var (
	JobnetManagerWatchFolders       = []string{"in"}
	FlowManagerWatchFolders         = []string{"in"}
	IconExecManagerWatchFolders     = []string{"in"}
	IconResultManagerWatchFolders   = []string{"in"}
	NotificationManagerWatchFolders = []string{"in"}
	DBSyncerManagerWatchFolders     = []string{"in"}
	AgentManagerWatchFolders        = []string{"in"}
)

// Managers plugin file path
const (
	JobnetManagerPluginPath       string = "/usr/local/bin/jobarranger/jobnet_manager.so"
	FlowManagerPluginPath         string = "/usr/local/bin/jobarranger/flow_manager.so"
	IconExecManagerPluginPath     string = "/usr/local/bin/jobarranger/icon_exec_manager.so"
	TrapperManagerPluginPath      string = "/usr/local/bin/jobarranger/trapper_manager.so"
	IconResultManagerPluginPath   string = "/usr/local/bin/jobarranger/icon_result_manager.so"
	ZabbixLinkManagerPluginPath   string = "/usr/local/bin/jobarranger/zabbix_link_manager.so"
	TimerManagerPluginPath        string = "/usr/local/bin/jobarranger/timer_manager.so"
	HousekeeperManagerPluginPath  string = "/usr/local/bin/jobarranger/housekeeper_manager.so"
	NotificationManagerPluginPath string = "/usr/local/bin/jobarranger/notification_manager.so"
	DBSyncerManagerPluginPath     string = "/usr/local/bin/jobarranger/dbsyncer_manager.so"
	RecoveryManagerPluginPath     string = "/usr/local/bin/jobarranger/recovery_manager.so"
	AgentManagerPluginPath        string = "/usr/local/bin/jobarranger/agent_manager.so"
	ZabbixVersionPluginPath       string = "/usr/local/bin/jobarranger/"
)

// Window agent manager parameters
const AgentExecFileName string = "agent_manager.exe"

// Managers socket file name
const (
	JobnetManagerSockFile       string = "jobnet_manager.sock"
	FlowManagerSockFile         string = "flow_manager.sock"
	IconExecManagerSockFile     string = "icon_exec_manager.sock"
	TrapperManagerSockFile      string = "trapper_manager.sock"
	IconResultManagerSockFile   string = "icon_result_manager.sock"
	ZabbixLinkManagerSockFile   string = "zabbix_link_manager.sock"
	TimerManagerSockFile        string = "timer_manager.sock"
	HousekeeperManagerSockFile  string = "housekeeper_manager.sock"
	NotificationManagerSockFile string = "notification_manager.sock"
	DBSyncerManagerSockFile     string = "dbsyncer_manager.sock"
	RecoveryManagerSockFile     string = "recovery_manager.sock"
	AgentManagerSockFile        string = "agent_manager.sock"
)

// EventName defines supported event types
type EventName string

const (
	// jobnet related events
	EventJobnetLoad                  EventName = "jobnet_load"
	EventStandAloneJob               EventName = "stand_alone_job"
	EventJobnetRun                   EventName = "jobnet_run"
	EventJobnetRunError              EventName = "jobnet_run_error"
	EventJobnetHold                  EventName = "jobnet_hold"
	EventJobnetStopping              EventName = "jobnet_stopping"
	EventJobnetEnding                EventName = "jobnet_ending"
	EventJobnetStatusChange          EventName = "jobnet_status_change"
	EventIconRerunJobnetStatusChange EventName = "icon_rerun_jobnet_status_change"
	EventJobnetTimeout               EventName = "jobnet_timeout"
	EventJobnetEnd                   EventName = "jobnet_end"
	EventSubJobnetEnd                EventName = "subjobnet_end"
	EventSubJobnetStatusChange       EventName = "subjobnet_status_change"
	EventIconRerunStatusSync         EventName = "icon_rerun_status_sync"
	EventJobnetDelayError            EventName = "jobnet_delay_error"

	// jobnet interaction events
	EventJobnetUnhold          EventName = "jobnet_unhold"
	EventScheduleDelete        EventName = "schedule_delete"
	EventJobnetStop            EventName = "jobnet_stop"
	EventJobnetIconStop        EventName = "jobnet_icon_stop"
	EventDelayedStart          EventName = "delayed_start"
	EventScheduleUpdate        EventName = "schedule_update"
	EventScheduleDeleteDBSync  EventName = "schedule_delete_dbsync"
	EventScheduleHoldDBSync    EventName = "schedule_hold_dbsync"
	EventScheduleReleaseDBSync EventName = "schedule_release_dbsync"
	EventScheduleUpdateDBSync  EventName = "schedule_update_dbsync"

	// icon related events
	EventIconReady                  EventName = "icon_ready"
	EventIconRun                    EventName = "icon_run"
	EventIconExecRun                EventName = "icon_exec_run"
	EventIconExecEnd                EventName = "icon_exec_end"
	EventOneWayIconRun              EventName = "one_way_icon_run"
	EventMIconRun                   EventName = "m_icon_run"
	EventTwoWayIconRun              EventName = "two_way_icon_run"
	EventJobnetIconRun              EventName = "jobnet_icon_run"
	EventFlowRunErr                 EventName = "flow_run_err"
	EventIconResultUpdate           EventName = "icon_result_update"
	EventIconResultEnd              EventName = "icon_result_end"
	EventIconResultRunErr           EventName = "icon_result_run_err"
	EventIconResultEndErr           EventName = "icon_result_end_err"
	EventIconTimeoutStop            EventName = "icon_result_timeout_stop"
	EventIconTimeoutWarning         EventName = "icon_result_timeout_warning"
	EventEndCountPlus               EventName = "end_count_plus"
	EventIconAbort                  EventName = "icon_abort"
	EventIconStatusChange           EventName = "icon_status_change"
	EventIconRetry                  EventName = "icon_retry"
	EventIconTimeout                EventName = "icon_timeout"
	EventIconTimeoutAbort           EventName = "icon_timeout_abort"
	EventExtIconFinish              EventName = "ext_icon_finish"
	EventEndIconEnd                 EventName = "end_icon_end"
	EventIconHold                   EventName = "icon_hold"
	EventIconHostUnlock             EventName = "icon_host_unlock"
	EventFlowJobnetIconStatusChange EventName = "flow_jobnet_icon_status_change"

	// icon interaction events
	EventIconUnhold        EventName = "icon_unhold"
	EventIconSkip          EventName = "icon_skip"
	EventIconRunErrSkip    EventName = "icon_runerr_skip"
	EventIconHoldSkip      EventName = "icon_hold_skip"
	EventIconStop          EventName = "icon_stop"
	EventIconRerun         EventName = "icon_rerun"
	EventFailoverIconRerun EventName = "failover_icon_rerun"
	EventFlowIconSkip      EventName = "flow_icon_skip"

	// Agent events
	EventAgentJobRun        EventName = "agent_job_run"
	EventAgentJobCheck      EventName = "agent_job_check"
	EventAgentJobRunResp    EventName = "agent_job_run_resp"
	EventAgentJobRan        EventName = "agent_job_ran"
	EventAgentJobResult     EventName = "agent_job_result"
	EventAgentJobResultResp EventName = "agent_job_result_resp"

	EventGetHostInfo EventName = "get_host_info"

	// zabbix icon events
	EventZabbixIconRun    EventName = "zabbix_icon_run"
	EventZabbixIconError  EventName = "zabbix_icon_error"
	EventZabbixIconEnd    EventName = "zabbix_icon_end"
	EventZabbixIconFinish EventName = "zabbix_icon_finish"
	EventZabbixHostIp     EventName = "get_zabbix_host_ip"

	// logger event
	EventInsertLogTable EventName = "insert_log_table"
	EventInsertSendMsg  EventName = "insert_send_msg"

	// zabbix send info insert
	EventInsertSendMsgTable EventName = "insert_send_msg_table"

	// check process
	EventFlowCheckLocal EventName = "flow_check_local"
	EventFlowCheckAgent EventName = "flow_check_agent"

	EventExecCheckLocal EventName = "exec_check_local" // others icon (not include job, fwait, reboot)
	EventExecCheckAgent EventName = "exec_check_agent" // job, fwait, reboot

	// DB SQL failure (non-retryable)
	EventDBJobnetRunQueryFailure EventName = "db_jobnet_run_query_failure"
	EventDBIconQueryFailure      EventName = "db_icon_query_failure"
)

// IconType represents job icon types in the UI
type IconType int

const (
	IconTypeStart  IconType = 0
	IconTypeEnd    IconType = 1
	IconTypeIf     IconType = 2
	IconTypeValue  IconType = 3
	IconTypeJob    IconType = 4
	IconTypeJobnet IconType = 5
	IconTypeM      IconType = 6
	IconTypeW      IconType = 7
	IconTypeL      IconType = 8
	IconTypeExtJob IconType = 9
	IconTypeCalc   IconType = 10
	IconTypeTask   IconType = 11
	IconTypeInfo   IconType = 12
	IconTypeIfEnd  IconType = 13
	IconTypeFCopy  IconType = 14
	IconTypeFWait  IconType = 15
	IconTypeReboot IconType = 16
	IconTypeRel    IconType = 17
	IconTypeLess   IconType = 18
	IconTypeLink   IconType = 19
	IconTypeAbort  IconType = 90

	IconTypeCheckJob IconType = 91
)

const (
	/* ja_2_session_table */
	/* operation flag */
	JA_SES_OPERATION_FLAG_ONETIME  = 0
	JA_SES_OPERATION_FLAG_CONNECT  = 1
	JA_SES_OPERATION_FLAG_CONTINUE = 2
	JA_SES_OPERATION_FLAG_CLOSE    = 3

	/* session status */
	JA_SES_STATUS_BEGIN = 0
	JA_SES_STATUS_END   = 1

	/* force stop */
	JA_SES_FORCE_STOP_OFF  = 0
	JA_SES_FORCE_STOP_ON   = 1
	JA_SES_FORCE_STOP_KILL = 2

	/* run mode */
	JA_RUN_MODE_INTERACTIVE     = 0
	JA_RUN_MODE_NON_INTERACTIVE = 1

	/* line feed code */
	JA_LINE_FEED_CODE_LF   = 0
	JA_LINE_FEED_CODE_CR   = 1
	JA_LINE_FEED_CODE_CRLF = 2
)

type JOBContinueFlag int

const (
	JOBContinueFlagOff = 0
	JOBContinueFlagOn  = 1
)

const (
	ZBXLINK_ZBX_LAST_STATUS = "ZBX_LAST_STATUS"
	ZBXLINK_ZBX_LATEST_DATA = "ZBX_LATEST_DATA"
	ZBXLINK_ZBX_DATA_TYPE   = "ZBX_DATA_TYPE"
)

// Job result related constants
const (
	JA_JOBRESULT_SUCCEED       = 0
	JA_JOBRESULT_FAIL          = 1
	JA_JOBRESULT_PRE_EXEC_FAIL = 2
	JA_JOBEXEC_IGNORE          = 3
	JA_JOBEXEC_FAIL            = 5
)

// SSH client related status
const (
	SSHD_CONNECT_SUCCESS = 0
	SSHD_CONNECT_FAIL    = 1
)

const (
	DataRecovery     = "Data recovery"
	DuplicateData    = "DUPLICATE DATA"
	AfterStatusError = "AFTER_STATUS ERROR"
)

const (
	RunExt = ".run"
)

type JACommand string

const (
	JACmdSleep JACommand = "jacmdsleep"
	JACmdTime  JACommand = "jacmdtime"
)

// Jobnet status constants
const (
	JAJobnetStatusBegin  = 0
	JAJobnetStatusReady  = 1
	JAJobnetStatusRun    = 2
	JAJobnetStatusEnd    = 3
	JAJobnetStatusRunErr = 4
	JAJobnetStatusEndErr = 5
)

// Jobnet job status constants (from ja_run_jobnet_summary_table)
const (
	JA_SUMMARY_JOB_STATUS_NORMAL  = 0
	JA_SUMMARY_JOB_STATUS_TIMEOUT = 1
	JA_SUMMARY_JOB_STATUS_ERROR   = 2
)

/* jobnet load status */
const (
	JA_SUMMARY_LOAD_STATUS_NORMAL = iota
	JA_SUMMARY_LOAD_STATUS_ERROR
	JA_SUMMARY_LOAD_STATUS_DELAY
	JA_SUMMARY_LOAD_STATUS_SKIP
)

type JobType int

const (
	JobTypeStart  = 0
	JobTypeEnd    = 1
	JobTypeIf     = 2
	JobTypeValue  = 3
	JobTypeJob    = 4
	JobTypeJobnet = 5
	JobTypeM      = 6
	JobTypeW      = 7
	JobTypeL      = 8
	JobTypeExtJob = 9
	JobTypeCalc   = 10
	JobTypeTask   = 11
	JobTypeInfo   = 12
	JobTypeIfEnd  = 13
	JobTypeFcopy  = 14
	JobTypeFwait  = 15
	JobTypeReboot = 16
	JobTypeRel    = 17
	JobTypeLess   = 18
	JobTypeLink   = 19

	// Skip to 90 for ABORT
	JOB_TYPE_ABORT JobType = 90
)

const (
	JA_CMD_SLEEP  = "jacmdsleep"
	JA_CMD_TIME   = "jacmdtime"
	JA_CMD_WEEK   = "jacmdweek"
	ZABBIX_SENDER = "zabbix_sender"
	MAX_DEST_LEN  = 8192
)

const (
	JA_JOB_TEST_FLAG_OFF = 0
	JA_JOB_TEST_FLAG_ON  = 1
)

const (
	JA_JOBNET_RUN_TYPE_NORMAL     = 0
	JA_JOBNET_RUN_TYPE_IMMEDIATE  = 1
	JA_JOBNET_RUN_TYPE_WAIT       = 2
	JA_JOBNET_RUN_TYPE_TEST       = 3
	JA_JOBNET_RUN_TYPE_SCHEDULED  = 4
	JA_JOBNET_RUN_TYPE_JOBALONE   = 5
	JA_JOBNET_RUN_TYPE_JOBNETICON = 6
)

const (
	JA_RUN_ID_JOBNET_LD = 1
	JA_RUN_ID_JOBNET_EX = 3
	JA_RUN_ID_JOB       = 20
	JA_RUN_ID_FLOW      = 30
	JA_RUN_ID_SESSION   = 40
)

type Flag int

const (
	FlagOff Flag = 0
	FlagOn  Flag = 1
)

// StatusType represents job execution status
type StatusType int

const (
	StatusBegin StatusType = iota
	StatusReady
	StatusRun
	StatusEnd
	StatusRunErr
	StatusEndErr
	StatusAbort // ending status

	// Fill gaps as needed or use explicit values below
)

type JobRunMethod int

const (
	MethodNormal JobRunMethod = iota
	MethodWait
	MethodSkip
	MethodAbort
	MethodRerun
)

type FlowType int

const (
	FlowNormal FlowType = 0
	FlowTrue   FlowType = 1
	FlowFalse  FlowType = 2
)

type FlowProcessType int

const (
	FlowProcessNormal            FlowProcessType = 0
	FlowParallelStartRun         FlowProcessType = 1
	FlowProcessSkip              FlowProcessType = 2
	FlowProcessUnhold            FlowProcessType = 3
	FlowProcessIconRerun         FlowProcessType = 4
	FlowProcessFailoverIconRerun FlowProcessType = 5
	FlowProcessSubJobnet         FlowProcessType = 6
	FlowProcessCheckJob          FlowProcessType = 7
	FlowProcessHostLockWait      FlowProcessType = 8
	FlowProcessAbort             FlowProcessType = 9
)

const (
	JsonRunFlowData       string = "run_flow_data"
	JsonRunJobData        string = "run_job_data"
	JsonRunJobVariable    string = "run_job_variable_data"
	JsonRunJobnetVariable string = "run_jobnet_variable_data"
	JsonIconDetailData    string = "icon_detail_data"
)

const (
	SelectQuery string = "select"
	UpdateQuery string = "update"
	DeleteQuery string = "delete"
)

// User interrupts from UI
type UserAction int

const (
	ActionIconUnhold UserAction = iota
	ActionIconRunErrSkip
	ActionIconHoldSkip
	ActionIconStop
	ActionIconRerun
	ActionJobnetStop
	ActionDelayedStart
	ActionScheduleUpdate
	ActionJobnetHold
	ActionJobnetUnhold
	ActionScheduleDelete
)

const (
	JC_JOBNET_START     = "JC00000001" // sql_flag = 0
	JC_JOBNET_END       = "JC00000002" // sql_flag = 0
	JC_JOB_START        = "JC00000003" // sql_flag = 1 (jobtype = 4)
	JC_JOB_END          = "JC00000004"
	JC_JOB_TIMEOUT      = "JC00000005"
	JC_JOB_SKIP         = "JC00000006"
	JC_JOB_RERUN        = "JC00000007"
	JC_JOBNET_TIMEOUT   = "JC00000008"
	JC_JOBNET_START_ERR = "JC90000001"
	JC_JOB_ERR_END      = "JC90000002" // sql_flag	= 2
	JC_JOBNET_ERR_END   = "JC90000003"
)

const (
	JA_SNT_SEND_STATUS_BEGIN = 0
	JA_SNT_SEND_STATUS_END   = 1
	JA_SNT_SEND_STATUS_RETRY = 2
	JA_SNT_SEND_STATUS_ERROR = 3
)

const (
	JA_PROTO_VALUE_JOBRESULT   string = "jobresult"
	JA_PROTO_VALUE_JOBRELEASE  string = "job.release"
	JA_PROTO_VALUE_JOBLOGPUT   string = "job.logput"
	JA_PROTO_VALUE_FCOPYRESULT string = "fcopy-res"
)

const (
	JA_RESPONSE_SUCCEED      = 0
	JA_RESPONSE_VERSION_FAIL = 1
	JA_RESPONSE_FAIL         = 2
	JA_RESPONSE_ALREADY_RUN  = 3
)

const (
	JA_FCOPY_FLAG_SUCCEED = '0'
	JA_FCOPY_FLAG_FAIL    = '1'
	JA_FCOPY_FLAG_NOFILE  = '2'
	JA_FCOPY_FLAG_KEEP    = '3'
)

const (
	KindJobRun       = "jobrun"
	KindJobRunRes    = "jobrun-res"
	KindJobResult    = "jobresult"
	KindJobResultRes = "jobresult-res"
	KindFileCopy     = "fcopy"
	KindFileCopyRes  = "fcopy-res"
	KindResponse     = "response"
	KindCheckJob     = "chkjob"
	KindIPChange     = "ipchange"
)

// Password encrypt version for job and agentless icons
const (
	EncryptVersion float64 = 2.2
)

// AgentJobStatus represents the status of an agent.
type AgentJobStatus int

const (
	AgentJobStatusBegin AgentJobStatus = iota
	AgentJobStatusRun
	AgentJobStatusEnd
	AgentJobStatusClose
	AgentJobStatusAbort = 6
)

type AgentMethod int

const (
	AgentMethodNormal AgentMethod = iota
	AgentMethodTest
	AgentMethodAbort
	AgentMethodKill
)

type AgentJobType string

const (
	AgentJobTypeCommand      AgentJobType = "command"
	AgentJobTypeReboot       AgentJobType = "reboot"
	AgentJobTypeExt          AgentJobType = "extjob"
	AgentJobTypeGetFile      AgentJobType = "getfile"
	AgentJobTypePutFile      AgentJobType = "putfile"
	AgentJobTypeCheckVersion AgentJobType = "check-version"
)

const (
	JobResultRespStatusFailed  int = -1 // 0
	JobResultRespStatusSuccess int = 0  // 1
)

type JaMainFlag int

const (
	JA_JOBNET_MAIN_FLAG_MAIN JaMainFlag = 0
	JA_JOBNET_MAIN_FLAG_SUB  JaMainFlag = 1
)

const JOBARG_TIME_FORMAT = "20060102150405"

type Event struct {
	Name      EventName `json:"name"`
	UniqueKey string    `json:"unique_key"`
	Priority  bool      `json:"priority"`
}

type NextProcess struct {
	Name ProcessType `json:"name"`
	Data interface{} `json:"data"`
	//Data map[string]string
}

type FileTransfer struct {
	Source      string `json:"source"`
	Destination string `json:"destination"`
}

type UDS struct {
	SocketPath string `json:"socket_path"`
}

type Transfer struct {
	Files []FileTransfer `json:"files"`
	UDS   UDS            `json:"uds"`
}

type QueryResult struct {
	Success      bool   `json:"success"`
	Error        string `json:"error,omitempty"`
	ErrorType    string `json:"error_type,omitempty"`
	RowsAffected int64  `json:"rows_affected"`
}

type DBSyncResult struct {
	Success      bool          `json:"success"`
	Error        string        `json:"error,omitempty"`
	ErrorType    string        `json:"error_type,omitempty"`
	RetryCount   int           `json:"retry_count"`
	Fatal        bool          `json:"fatal,omitempty"`
	StartTime    string        `json:"start_time,omitempty"`
	EndTime      string        `json:"end_time,omitempty"`
	Duration     time.Duration `json:"duration,omitempty"`
	QueryResults []QueryResult `json:"query_results,omitempty"`
}

type Operator string

const (
	OpEq Operator = "=="
	OpNe Operator = "!="
	OpLt Operator = "<"
	OpLe Operator = "<="
	OpGt Operator = ">"
	OpGe Operator = ">="
)

type Action string

const (
	ActionWait   Action = "wait"
	ActionExec   Action = "exec"
	ActionIgnore Action = "ignore"
	ActionError  Action = "error"
)

// FieldCondition represents condition checks for a single DB field/column.
type FieldCondition struct {
	Values   []any    `json:"values"`
	Operator Operator `json:"operator"`
}

// Condition is one entry in the conditions array.
type Condition struct {
	Fields map[string]FieldCondition `json:"fields"`
	Action Action                    `json:"action"`
}

// SQLCondition ties a query to a set of conditions.
type SQLCondition struct {
	SQL           string      `json:"sql"`
	Conditions    []Condition `json:"conditions"`
	DefaultAction Action      `json:"default_action"`
}

type ParameterizedQuery struct {
	SQL        string `json:"sql"`
	Parameters []any  `json:"parameters"`
}

type EventData struct {
	Event                Event                `json:"event"`
	NextProcess          NextProcess          `json:"next_process"`
	TCPMessage           *TCPMessage          `json:"tcp_message,omitempty"`
	SQLConditions        []SQLCondition       `json:"sql_conditions"`
	ParameterizedQueries []ParameterizedQuery `json:"parameterized_queries"`
	DBSyncResult         *DBSyncResult        `json:"dbsync_result"`
	Transfer             Transfer             `json:"transfer"`
}

func (ed *EventData) AddPQ(sql string, params ...any) {
	ed.ParameterizedQueries = append(ed.ParameterizedQueries, ParameterizedQuery{
		SQL:        sql,
		Parameters: params,
	})
}

func (ed *EventData) AppendPQs(PQs ...ParameterizedQuery) {
	ed.ParameterizedQueries = append(ed.ParameterizedQueries, PQs...)
}

func GetPQ(sql string, params ...any) ParameterizedQuery {
	return ParameterizedQuery{
		SQL:        sql,
		Parameters: params,
	}
}

type Data struct {
	EventData     []byte
	DBConn        database.DBConnection
	DB            database.Database
	NetConn       *NetConnection
	EventRoute    int
	EventFileName string
}

type IconResultData struct {
	Type          int    `json:"icon_type"`
	InnerJobID    uint64 `json:"inner_job_id"`
	InnerJobnetID uint64 `json:"inner_jobnet_id"`
}

type IconDetailData struct {
	Type                int    `json:"icon_type"`
	InnerJobID          uint64 `json:"inner_job_id"`
	InnerJobnetID       uint64 `json:"inner_jobnet_id"`
	HostFlag            int    `json:"host_flag"`
	ConnectionMethod    int    `json:"connection_method"`
	SessionFlag         int    `json:"session_flag"`
	AuthMethod          int    `json:"auth_method"`
	RunMode             int    `json:"run_mode"`
	LineFeedCode        int    `json:"line_feed_code"`
	Timeout             int    `json:"timeout"`
	SessionID           string `json:"session_id"`
	LoginUser           string `json:"login_user"`
	LoginPassword       string `json:"login_password"`
	PublicKey           string `json:"public_key"`
	PrivateKey          string `json:"private_key"`
	Passphrase          string `json:"passphrase"`
	HostName            string `json:"host_name"`
	StopCode            string `json:"stop_code"`
	TerminalType        string `json:"terminal_type"`
	CharacterCode       string `json:"character_code"`
	PromptString        string `json:"prompt_string"`
	Command             string `json:"command"`
	HandFlag            int    `json:"hand_flag"`
	Formula             string `json:"formula"`
	ValueName           string `json:"value_name"`
	JobnetStopFlag      int    `json:"jobnet_stop_flag"`
	JobnetStopCode      string `json:"jobnet_stop_code"`
	CommandID           string `json:"command_id"`
	Value               string `json:"value"`
	PID                 int    `json:"pid"`
	WaitCount           int    `json:"wait_count"`
	WaitTime            string `json:"wait_time"`
	FromHostFlag        int    `json:"from_host_flag"`
	ToHostFlag          int    `json:"to_host_flag"`
	OverwriteFlag       int    `json:"overwrite_flag"`
	FromHostName        string `json:"from_host_name"`
	FromDirectory       string `json:"from_directory"`
	FromFileName        string `json:"from_file_name"`
	ToHostName          string `json:"to_host_name"`
	ToDirectory         string `json:"to_directory"`
	FWaitModeFlag       int    `json:"fwait_mode_flag"`
	FileDeleteFlag      int    `json:"file_delete_flag"`
	FileWaitTime        int    `json:"file_wait_time"`
	FileName            string `json:"file_name"`
	ComparisonValue     string `json:"comparison_value"`
	InfoFlag            int    `json:"info_flag"`
	ItemID              int    `json:"item_id"`
	TriggerID           int    `json:"trigger_id"`
	HostGroup           string `json:"host_group"`
	GetJobID            string `json:"get_job_id"`
	GetCalendarID       string `json:"get_calendar_id"`
	LinkInnerJobnetID   uint64 `json:"link_inner_jobnet_id"`
	LinkJobnetID        string `json:"link_jobnet_id"`
	StopFlag            int    `json:"stop_flag"`
	CommandType         int    `json:"command_type"`
	TimeoutRunType      int    `json:"timeout_run_type"`
	CommandCls          int    `json:"command_cls"`
	RebootModeFlag      int    `json:"reboot_mode_flag"`
	RebootWaitTime      int    `json:"reboot_wait_time"`
	ReleaseJobID        string `json:"release_job_id"`
	SubmitInnerJobnetID uint64 `json:"submit_inner_jobnet_id"`
	SubmitJobnetID      string `json:"submit_jobnet_id"`
	LinkTarget          int    `json:"link_target"`
	LinkOperation       int    `json:"link_operation"`
	GroupID             int    `json:"groupid"`
	HostID              int    `json:"hostid"`
	ItemIDFinal         int    `json:"itemid"`
	TriggerIDFinal      int    `json:"triggerid"`
}

var (
	ServerTmpFolderPath = "/var/lib/jobarranger/server/"
	AgentTmpFolderPath  = "/var/lib/jobarranger/agent/"
	ConfigFilePath      string
	LockFilePath        string
)

func GetUniqueKey(processName ProcessType) string {
	return fmt.Sprintf("%s_%d", processName, time.Now().UnixNano())
}

func WriteStructToFD3(data any) error {
	fd := 3
	file := os.NewFile(uintptr(fd), "fd3")
	if file == nil {
		return fmt.Errorf("failed to open fd3")
	}
	defer file.Close()

	encoder := json.NewEncoder(file)
	if err := encoder.Encode(data); err != nil {
		return fmt.Errorf("failed to encode data to fd3: %w", err)
	}
	return nil
}

func SetClientPid(pid int) error {
	fd := 4

	file := os.NewFile(uintptr(fd), "fd4")
	if file == nil {
		return fmt.Errorf("failed to open fd4")
	}
	defer file.Close()

	encoder := json.NewEncoder(file)
	if err := encoder.Encode(pid); err != nil {
		return fmt.Errorf("failed to encode data to fd4: %w", err)
	}
	return nil
}

// Define Zabbix params struct

type ZabbixParams struct {
	Username string `json:"username"`
	Password string `json:"password"`
}

type ZabbixResponse struct {
	JSONRPC string      `json:"jsonrpc"`
	Result  string      `json:"result"`
	ID      interface{} `json:"id"`
}

// ZabbixRequest represents the request structure for Zabbix API
type ZabbixRequest struct {
	JSONRPC string `json:"jsonrpc"`
	Method  string `json:"method"`
	Params  any    `json:"params,omitempty"` // <== change here
	Auth    string `json:"auth,omitempty"`
	ID      int    `json:"id"`
}

// RequestPayload is the top-level JSON structure
type RequestPayload struct {
	Zabbix      ZabbixRequest  `json:"Zabbix"`
	JobArranger map[string]any `json:"JobArranger"` // ✅ correct type
}

type JobLog struct {
	LogDate           string  `json:"log_date"`
	InnerJobnetMainID string  `json:"inner_jobnet_main_id"`
	InnerJobnetID     string  `json:"inner_jobnet_id"`
	JobnetID          string  `json:"jobnet_id"`
	RunType           string  `json:"run_type"`
	PublicFlag        string  `json:"public_flag"`
	JobID             *string `json:"job_id"`
	MessageID         string  `json:"message_id"`
	Message           string  `json:"message"`
	JobnetName        string  `json:"jobnet_name"`
	JobName           *string `json:"job_name"`
	UserName          string  `json:"user_name"`
	UpdateDate        string  `json:"update_date"`
	ExistCd           string  `json:"exist_cd"`
}

type InnerJobnetIdList struct {
	InnerJobnetId []string `json:"jobReleaseInnerJobnetId"`
}

type ZabbixOrJobResult struct {
	ZabbixAuth     string
	JobResult      *bool // use pointer to allow "nil" when not set
	RegistryNumber string

	RunType           *int
	Status            string
	JobStatus         *int
	ScheduledTime     *int64
	StartTime         *int64
	EndTime           *int64
	JobnetName        string
	JobnetId          string
	InnerJobId        string
	StdOut            string
	StdErr            string
	JobExitCd         string
	LogList           []JobLog
	InnerJobnetIdList InnerJobnetIdList
}

type JobArrangerResponse struct {
	Result                  bool     `json:"result"`
	RunType                 *int     `json:"runType"`
	RegistryNumber          string   `json:"registry_number"` // ✅ fix here
	Status                  string   `json:"status"`
	JobStatus               *int     `json:"jobStatus"`
	ScheduledTime           *int64   `json:"scheduledTime"`
	StartTime               *int64   `json:"startTime"`
	EndTime                 *int64   `json:"endTime"`
	JobnetName              string   `json:"jobnetName"`
	JobnetId                string   `json:"jobnetId"`
	InnerJobId              string   `json:"innerJobId"`
	StdOut                  string   `json:"stdOut"`
	StdErr                  string   `json:"stdErr"`
	JobExitCd               string   `json:"jobExitCd"`
	Log                     JobLog   `json:"log"`
	JobReleaseInnerJobnetId []string `json:"jobReleaseInnerJobnetId"`
}

type FullResponse struct {
	Zabbix      ZabbixResponse      `json:"Zabbix"`
	JobArranger JobArrangerResponse `json:"JobArranger"`
}

type ErrorResponseWithMsg struct {
	Error struct {
		Type    string `json:"type"`
		Message string `json:"message"`
	} `json:"Error"`
}

// TruncateTo64KB returns a UTF-8 safe string trimmed to max 64 KB (65536 bytes).
func TruncateTo64KB(s string) string {
	const limit = 64 * 1024 // 64 KB = 65536 bytes

	b := []byte(s)
	if len(b) <= limit {
		return s
	}

	// Cut to limit first
	b = b[:limit]

	// Ensure we do NOT end in the middle of a UTF-8 multi-byte sequence
	for len(b) > 0 && (b[len(b)-1]&0xC0) == 0x80 {
		b = b[:len(b)-1]
	}

	return string(b)
}
