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

import (
	"bytes"
	"encoding/json"
	"fmt"
	"strconv"

	"jobarranger2/src/libs/golibs/common"
	"jobarranger2/src/libs/golibs/logger/logger"
	"jobarranger2/src/libs/golibs/utils"

	clientcommon "jobarranger2/src/jobarg_server/managers/icon_exec_manager/workers/common"
)

func UnmarshalAs[T any](data []byte) (T, error) {
	var t T
	dec := json.NewDecoder(bytes.NewReader(data))
	dec.UseNumber() // prevents float64 coercion
	err := dec.Decode(&t)
	return t, err
}

func UnmarshalEventOrTCPMessage(data []byte) (*common.EventData, int, error) {
	fn := "UnmarshalEventOrTCPMessage"
	var ed common.EventData
	var jazVersion int
	var errMsg string

	tcp, err := UnmarshalAs[common.TCPMessage](data)
	if err == nil && isValidTCPMessage(tcp) {
		jazVersion = 1
		tcp.JazVersion = &jazVersion
		switch tcp.Kind {
		case common.KindJobRun, common.KindFileCopy:
			var tcpData common.JobRunRequestData
			err := utils.Convert(tcp.Data, &tcpData)
			if err != nil {
				return nil, 0, fmt.Errorf("type convert to 'common.JobRunRequestData' failed: %v", err)
			}

			tcp.Data = tcpData
		case common.KindJobResult:
			var tcpData common.JobResultData
			err := utils.Convert(tcp.Data, &tcpData)
			if err != nil {
				return nil, 0, fmt.Errorf("type convert to 'common.JobResultData' failed: %v", err)
			}

			tcp.Data = tcpData
		}

		ed = common.EventData{
			TCPMessage: &tcp,
		}

		return &ed, jazVersion, nil
	}

	if err != nil {
		errMsg += fmt.Sprintf("not tcp data: %s ", err.Error())
	} else {
		errMsg += "not tcp data "
	}

	ed, err = UnmarshalAs[common.EventData](data)
	if err == nil {
		if isValidEventData(ed) {
			err = utils.UnmarshalEventData(data, &ed)
			if err != nil {
				return nil, 0, fmt.Errorf("failed to unmarshal event data: %v", err)
			}
			jazVersion = 2
		} else {
			jazVersion = 1
		}
		ed.TCPMessage.JazVersion = &jazVersion

		switch ed.TCPMessage.Kind {
		case common.KindJobRun, common.KindFileCopy:
			var tcpData common.JobRunRequestData
			err := utils.Convert(ed.TCPMessage.Data, &tcpData)
			if err != nil {
				return nil, 0, fmt.Errorf("type convert to 'common.JobRunRequestData' failed: %v", err)
			}

			ed.TCPMessage.Data = tcpData
		case common.KindJobResult:
			var tcpData common.JobResultData
			err := utils.Convert(ed.TCPMessage.Data, &tcpData)
			if err != nil {
				return nil, 0, fmt.Errorf("type convert to 'common.JobResultData' failed: %v", err)
			}

			ed.TCPMessage.Data = tcpData
		}
		return &ed, jazVersion, nil
	}

	errMsg += fmt.Sprintf("not event data: %s", err.Error())

	logger.WriteLog("JAAGENTMNG200038", fn, errMsg)

	return nil, 0, fmt.Errorf("invalid data: not EventData or TCPMessage")
}

func isValidEventData(ed common.EventData) bool {
	// Basic validity check
	return ed.Event.Name != ""
}

func isValidTCPMessage(t common.TCPMessage) bool {
	// Basic validity check
	return t.Kind != ""
}

func ParseAnyToInt(val any) (int, error) {
	var overwriteInt int
	var err error
	switch v := val.(type) {
	case string:
		overwriteInt, err = strconv.Atoi(v)
		if err != nil {
			return -1, fmt.Errorf("invalid overwrite string: %v", err)
		}

	case json.Number:
		overwriteInt64, err := v.Int64()
		if err != nil {
			return -1, fmt.Errorf("invalid overwrite number: %v", err)
		}
		overwriteInt = int(overwriteInt64)

	case float64:
		// fallback if you didn’t use decoder.UseNumber()
		overwriteInt = int(v)

	default:
		return -1, fmt.Errorf("unexpected type for overwrite: %T (%v)", v, v)
	}

	return overwriteInt, nil
}

// This function prepares TCP data from event data and assign it to EventData.TCPMessage
func PrepareTCPDataFromEventData(eventData *common.EventData) error {
	fn := "PrepareTCPDataFromEventData"

	executionData, ok := eventData.NextProcess.Data.(common.IconExecutionProcessData)
	if !ok {
		return fmt.Errorf("[%s] failed to assert next process data as %T", fn, executionData)
	}

	if eventData.Event.Name == common.EventAgentJobCheck {
		return nil
	}

	jobRunRequestData, ok := eventData.TCPMessage.Data.(common.JobRunRequestData)
	if !ok {
		return fmt.Errorf("[%s] failed to assert tcp data data as %T", fn, jobRunRequestData)
	}

	clientcommon.BeforeVariableMu.Lock()
	defer clientcommon.BeforeVariableMu.Unlock()

	clientcommon.BeforeVariableMap = make(map[string]string)

	if err := clientcommon.PrepareJobRunRequest(executionData, &jobRunRequestData); err != nil {
		return fmt.Errorf("[%s] failed to extract job run request: %v", fn, err)
	}

	// assigning the tcp data extracted from icon execution process data
	eventData.TCPMessage.Data = jobRunRequestData

	return nil
}
