package main

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

	"jobarranger2/src/libs/golibs/common"
	"jobarranger2/src/libs/golibs/event"
	"jobarranger2/src/libs/golibs/utils"
)

func trapperHandler(jobnetPayload common.EventData, tcpCon *common.NetConnection) error {
	funcname := "trapperHandler"
	TrapperLogger.JaLog("JATRAPPERMANAGER400001", funcname)

	var tcpObj *common.TCPMessage
	if jobnetPayload.TCPMessage != nil {
		tcpObj = jobnetPayload.TCPMessage
	} else {
		TrapperLogger.JaLog("JATRAPPERMANAGER200002")
		return fmt.Errorf("tcp message is nil")
	}

	//prepare tcp response message
	tcpResponse := common.ResponseData{
		Result: 0,
	}

	// Usage in switch
	switch tcpObj.Kind {
	case common.JA_PROTO_VALUE_JOBRESULT:
		_, err := AssignTypedData[common.JobResultData](tcpObj)
		if err != nil {
			TrapperLogger.JaLog("JATRAPPERMANAGER200003", err.Error())
			return fmt.Errorf("assignTypedData failed")
		}
		tcpJobResultData, ok := jobnetPayload.TCPMessage.Data.(common.JobResultData)
		if !ok {
			TrapperLogger.JaLog("JATRAPPERMANAGER200004", "JobResultData")
			return fmt.Errorf("type assertion failed")
		}

		if jaTrapAuthHost(tcpObj.Hostname, &tcpResponse) {
			ok, err := jaTrapJobResult(tcpJobResultData, &tcpResponse)
			if err != nil {
				TrapperLogger.JaLog("JATRAPPERMANAGER200001", funcname, err.Error())
				return fmt.Errorf("jaTrapJobResult failed")
			}
			if !ok {
				return jaTrapperReply(jobnetPayload, tcpCon, tcpResponse)
			}
		} else {
			TrapperLogger.JaLog("JATRAPPERMANAGER200005", tcpObj.Hostname)
			jaTelegramSetErr(&tcpResponse, fmt.Sprintf("Host '%s' is not authenticated.", tcpObj.Hostname), "")
			return jaTrapperReply(jobnetPayload, tcpCon, tcpResponse)
		}

	case common.JA_PROTO_VALUE_FCOPYRESULT:
		_, err := AssignTypedData[common.JobResultData](tcpObj)
		if err != nil {
			TrapperLogger.JaLog("JATRAPPERMANAGER200003", err.Error())
			return nil
		}

	default:
		// Optionally handle unknown kinds
	}

	err := createEventForIconResManager(jobnetPayload, tcpCon, &tcpResponse)
	if err != nil {
		TrapperLogger.JaLog("JATRAPPERMANAGER200001", "createEventForIconResManager", err.Error())
		return fmt.Errorf("createEventForIconResManager failed")
	}

	return nil
}

func jaTrapJobResult(jobresultData common.JobResultData, tcpResponse *common.ResponseData) (bool, error) {
	funcname := "jaTrapJobResult"
	TrapperLogger.JaLog("JATRAPPERMANAGER400001", funcname)
	var errMsg string
	indexFilePath := filePathForTmpJobServer(jobresultData.JobID)

	TrapperLogger.JaLog("JATRAPPERMANAGER400002", jobresultData.JobID, jobresultData.PreUniqueID, jobresultData.CurUniqueID)
	fileUid, err := readLastLineOrNew(indexFilePath)
	if err != nil {
		TrapperLogger.JaLog("JATRAPPERMANAGER200001", funcname, err.Error())
		return false, fmt.Errorf("readLastLineOrNew failed")
	}

	//if file does not exist
	if fileUid == NEW {
		//file delete during processing
		if jobresultData.PreUniqueID != NEW {
			TrapperLogger.JaLog("JATRAPPERMANAGER400003", indexFilePath)
		}

		//add pre_uid to file
		if err := addUidToJobFile(indexFilePath, jobresultData.PreUniqueID); err != nil {
			TrapperLogger.JaLog("JATRAPPERMANAGER200001", "addUidToJobFile", err.Error())
			return false, fmt.Errorf("failed to add pre_unique_id to job file: %w", err)
		}
	}
	// Check for data recovery or duplicate data situations
	if fileUid != NEW {
		if jobresultData.PreUniqueID == NEW {
			// Case: Index file exists, but PreUniqueID is NEW (possible recovery needed)
			TrapperLogger.JaLog("JATRAPPERMANAGER300001", funcname, indexFilePath)
			errMsg = DATA_RECOVERY
		} else if fileUid != jobresultData.PreUniqueID {
			// Case: UID in file does not match PreUniqueID (duplicate or corrupt)
			TrapperLogger.JaLog("JATRAPPERMANAGER400004", fileUid)
			errMsg = DUPLICATE_DATA
		}

		if errMsg != "" {
			jaTelegramSetErr(tcpResponse, errMsg, fileUid)
			return false, nil
		}
	}

	//add current_uid to file
	if err := addUidToJobFile(indexFilePath, jobresultData.CurUniqueID); err != nil {
		TrapperLogger.JaLog("JATRAPPERMANAGER200001", "addUidToJobFile", err.Error())
		return false, fmt.Errorf("failed to add cur_unique_id to job file: %w", err)
	}
	TrapperLogger.JaLog("JATRAPPERMANAGER400005", jobresultData.CurUniqueID)

	return true, nil
}

// Main handler: sends via UDS and file, then responds via TCP
func createEventForIconResManager(jobnetPayload common.EventData, con *common.NetConnection, resp *common.ResponseData) error {
	funcname := "createEventForIconResManager"
	TrapperLogger.JaLog("JATRAPPERMANAGER400001", funcname)
	//prepare data for iconresultmanager
	jobnetPayload.Event.Name = common.EventIconResultUpdate
	jobnetPayload.Event.UniqueKey = common.GetUniqueKey(common.TrapperManagerProcess)

	// For response back to agent
	var iconExecProcessData common.IconExecutionProcessData

	responsePayload := jobnetPayload

	//if jazversion is 2
	if jobnetPayload.TCPMessage.JazVersion != nil {
		if data, ok := jobnetPayload.NextProcess.Data.(common.IconExecutionProcessData); ok {
			iconExecProcessData = data
		} else {
			TrapperLogger.JaLog("JATRAPPERMANAGER200004", "IconExecutionProcessData")
			return fmt.Errorf("type assertion failed")
		}
	} else {
		jobnetPayload.NextProcess.Data = iconExecProcessData
	}

	switch jobnetPayload.TCPMessage.Kind {
	case common.JA_PROTO_VALUE_JOBRESULT:
		tcpJobResultData, ok := jobnetPayload.TCPMessage.Data.(common.JobResultData)
		if !ok {
			TrapperLogger.JaLog("JATRAPPERMANAGER200004", "JobResultData")
			return fmt.Errorf("type assertion failed")
		}
		iconExecProcessData.JobResult = tcpJobResultData

	case common.JA_PROTO_VALUE_FCOPYRESULT:
		fcopyResultData, ok := jobnetPayload.TCPMessage.Data.(common.JobResultData)
		if !ok {
			TrapperLogger.JaLog("JATRAPPERMANAGER200004", "ResponseData")
			return fmt.Errorf("type assertion failed")
		}
		iconExecProcessData.JobResult = fcopyResultData
	}

	var (
		innerJobnetID uint64
		innerJobID    uint64
		err           error
		jobnetId      string
	)

	if jobnetPayload.TCPMessage != nil && jobnetPayload.TCPMessage.JazVersion == nil {
		// JAZ 1: Parse JobID from string
		innerJobnetID = 0
		innerJobID, err = strconv.ParseUint(iconExecProcessData.JobResult.JobID, 10, 64)
		if err != nil {
			return fmt.Errorf("%s", err.Error())
		}
		iconExecProcessData.JobResult.JazVersion = JAZ_1_VERSION
	} else {
		innerJobnetID = iconExecProcessData.RunJobData.InnerJobnetID
		innerJobID = iconExecProcessData.RunJobData.InnerJobID

		type BeforeVariable struct {
			JobnetId string `json:"JOBNET_ID"`
		}

		var before BeforeVariable
		err = json.Unmarshal(iconExecProcessData.RunJobVariableData.BeforeVariable, &before)
		if err != nil {
			return fmt.Errorf("%s", err.Error())
		}
		jobnetId = before.JobnetId
		iconExecProcessData.JobResult.JazVersion = JAZ_2_VERSION
	}

	jobnetPayload.NextProcess.Data = iconExecProcessData
	jobnetPayload.Transfer.Files = []common.FileTransfer{}

	err = event.CreateNextEvent(jobnetPayload, innerJobnetID, jobnetId, innerJobID)
	if err != nil {
		jaTelegramSetErr(resp, "Failed to create next event file", "")
	}

	return jaTrapperReply(responsePayload, con, *resp)
}

func AssignTypedData[T any](tcpObj *common.TCPMessage) (T, error) {
	var target T
	if tcpObj.JazVersion == nil {
		err := utils.Convert(tcpObj.Data, &target)
		if err != nil {
			TrapperLogger.JaLog("JATRAPPERMANAGER200001", "AssignTypedData", err.Error())
			return target, fmt.Errorf("MapToStruct failed: %w", err)
		}
		tcpObj.Data = target
	}
	return target, nil
}
