/*
** 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 (
	"encoding/json"
	"path/filepath"
	"time"

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

const (
	ErrorFolder = "error"
	EndFolder   = "end"
)

func main() {

}

func StartDaemonWorkers(data common.Data) {

}

// This function process the data received from event in the following way:
//   - Execute all the queries in the EventData under a transaction
//   - Move the source file (that contains EventData) to the destination directory
//
// Note: if any of the operation has failed, it will rollback the DB
func ProcessEventData(data common.Data) {
	const fn = "ProcessEventData"
	logData := logger.Logging{}

	// Unmarshaling the data
	var eventData common.EventData
	err := utils.UnmarshalEventData(data.EventData, &eventData)
	if err != nil {
		var tmpEventData common.EventData
		if err := json.Unmarshal(data.EventData, &tmpEventData); err != nil {
			logger.JaLog("JADBSYNCER200001", logData, fn, "unknown", string(data.EventData), err.Error())
			return
		}

		filePath, err := utils.GetFirstFileSource(tmpEventData.Transfer.Files)
		if err != nil {
			logger.JaLog("JADBSYNCER200026", logData, fn, "unknown", err.Error())
			return
		}

		filename := filepath.Base(filePath)

		// unregistered event in the common func
		logger.JaLog("JADBSYNCER200019", logData, fn, filename, tmpEventData.Event.Name)

		// move to error folder
		moveFilesToDBSyncSubFolder(eventData, logData, filename, ErrorFolder) // logs internally

		return
	}

	filePath, err := utils.GetFirstFileSource(eventData.Transfer.Files)
	if err != nil {
		logger.JaLog("JADBSYNCER200026", logData, fn, "unknown", err.Error())
		return
	}
	// Get filename for logging
	filename := filepath.Base(filePath)

	logData, err = prepareLogData(filename)
	if err != nil {
		logger.JaLog("JADBSYNCER200013", logData, fn, filename, err.Error())
	}

	// Check queries length
	if len(eventData.ParameterizedQueries) == 0 {
		logger.JaLog("JADBSYNCER200010", logData, fn, filename)
		moveFilesToDBSyncSubFolder(eventData, logData, filename, ErrorFolder) // logs internally
		return
	}

	logger.JaLog("JADBSYNCER400002", logData, fn, filename, len(eventData.ParameterizedQueries))

	// Handle for run and end event timing
	if err := handleSQLConditions(&eventData, data.DBConn, logData); err != nil {
		switch e := err.(type) {
		case *TimingError:
			logger.JaLog("JADBSYNCER400022", logData, fn, filename, e.Error())
		default:
			// generic error; move to error folder
			logger.JaLog("JADBSYNCER200017", logData, fn, filename, e.Error())
			moveFilesToDBSyncSubFolder(eventData, logData, filename, ErrorFolder) // logs internally
		}

		return
	}

	// Start DB Transaction
	err = data.DBConn.Begin()
	if err != nil {
		logger.JaLog("JADBSYNCER200002", logData, fn, filename, err.Error())
		return
	}
	logger.JaLog("JADBSYNCER400011", logData, fn, filename)

	// To store query results
	eventData.DBSyncResult.QueryResults = make([]common.QueryResult, 0, len(eventData.ParameterizedQueries))

	start := time.Now()
	eventData.DBSyncResult.StartTime = start.UTC().Format(time.RFC3339)

	// Executing all queries
	for _, query := range eventData.ParameterizedQueries {
		result, err := data.DBConn.Execute(query.SQL, query.Parameters...)

		qr := common.QueryResult{
			Success: err == nil,
		}
		if err == nil {
			qr.RowsAffected = int64(result)
			logger.JaLog("JADBSYNCER400009", logData, fn, filename, query, result)
			eventData.DBSyncResult.QueryResults = append(eventData.DBSyncResult.QueryResults, qr)
			continue
		}

		qr.Error = err.Error()
		eventData.DBSyncResult.QueryResults = append(eventData.DBSyncResult.QueryResults, qr)
		logger.JaLog("JADBSYNCER200003", logData, fn, filename, query, err.Error())

		if err := handleDBError(err, data.DBConn, &eventData, logData, filePath, query.SQL); err != nil {
			switch e := err.(type) {
			case *TimingError:
				logger.JaLog("JADBSYNCER400029", logData, fn, filename, e.Error())
			case *NonRetryableError:
				// non-retryable error; move to error folder
				logger.JaLog("JADBSYNCER400029", logData, fn, filename, e.Error())
				moveFilesToDBSyncSubFolder(eventData, logData, filename, ErrorFolder) // logs internally
			default:
				// generic error; move to error folder
				logger.JaLog("JADBSYNCER200025", logData, fn, filename, e.Error())
				moveFilesToDBSyncSubFolder(eventData, logData, filename, ErrorFolder) // logs internally
			}
		}

		return
	}

	logger.JaLog("JADBSYNCER400003", logData, fn, filename)

	// Prepare DBSync result
	eventData.DBSyncResult.Success = true
	eventData.DBSyncResult.EndTime = time.Now().UTC().Format(time.RFC3339)
	eventData.DBSyncResult.Duration = time.Duration(time.Since(start).Milliseconds())

	err = data.DBConn.Commit()
	if err != nil {
		logger.JaLog("JADBSYNCER200004", logData, fn, filename, err.Error())
		if err := handleDBError(err, data.DBConn, &eventData, logData, filePath, "commit"); err != nil {
			switch e := err.(type) {
			case *TimingError:
				logger.JaLog("JADBSYNCER400029", logData, fn, filename, e.Error())
			case *NonRetryableError:
				// non-retryable error; move to error folder
				logger.JaLog("JADBSYNCER400029", logData, fn, filename, e.Error())
				moveFilesToDBSyncSubFolder(eventData, logData, filename, ErrorFolder) // logs internally
			default:
				// generic error; move to error folder
				logger.JaLog("JADBSYNCER200025", logData, fn, filename, e.Error())
				moveFilesToDBSyncSubFolder(eventData, logData, filename, ErrorFolder) // logs internally
			}
		}

		return
	}
	logger.JaLog("JADBSYNCER400012", logData, fn, filename)

	// Move files to corresponding destinations
	err = moveFiles(eventData, logData, filename)
	if err != nil {
		return // moveFiles already logs internally
	}

	// Check socket path
	if eventData.Transfer.UDS.SocketPath == "" {
		logger.JaLog("JADBSYNCER400001", logData, fn, filename)
		return
	}

	logger.JaLog("JADBSYNCER400008", logData, fn, filename, eventData.Transfer.UDS.SocketPath)

	err = uds.SendViaUDS(eventData.Transfer.UDS.SocketPath, &eventData)
	if err != nil {
		logger.JaLog("JADBSYNCER200009", logData, fn, filename, eventData.Transfer.UDS.SocketPath, err.Error())
		return
	}
	logger.JaLog("JADBSYNCER400014", logData, fn, filename, eventData.Transfer.UDS.SocketPath)
}
