/*
** 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 (
	"flag"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"runtime/debug"
	"strconv"
	"strings"

	"jobarranger2/src/jobarg_agent/managers/agent_manager/agentutils"
	"jobarranger2/src/libs/golibs/common"
	"jobarranger2/src/libs/golibs/config_reader/agent"
	"jobarranger2/src/libs/golibs/config_reader/conf"
	"jobarranger2/src/libs/golibs/forker"
	"jobarranger2/src/libs/golibs/logger/logger"
	"jobarranger2/src/libs/golibs/utils"
)

var inFolderPath, ExecFolderPath string
var jobRunFilePath *string
var extJobPath string
var RebootFlagFilePrefix, RebootScriptFile string

var RebootScriptFileName = "jareboot." + agentutils.GetScriptExtension()

func main() {
	fn := "job-run-client:main"
	configFilePath := flag.String("config-file-path", "", "Full path to the jaz agent config")
	jobRunFilePath = flag.String("job-run-file-path", "", "Full path to the job run file")
	common.Manager.Name = "job_run_client"

	flag.Parse()

	if *configFilePath == "" {
		fmt.Fprintf(os.Stderr, "Error: -config-file-path is required [pid=%d]\n", os.Getpid())
		os.Exit(1)
	}

	if *jobRunFilePath == "" {
		fmt.Fprintf(os.Stderr, "Error: -job-run-file-path is required [pid=%d]\n", os.Getpid())
		os.Exit(1)
	}

	// Load config
	if err := conf.Load(*configFilePath, &agent.Options); err != nil {
		fmt.Fprintf(os.Stderr, "In %s(), failed to load config file [pid=%d]: %v\n", fn, os.Getpid(), err)
		os.Exit(1)
	}

	// Init logger
	if err := logger.InitLogger(
		agent.Options.JaLogFile,
		agent.Options.JaLogMessageFile,
		agent.Options.LogType,
		agent.Options.LogFileSize*1024*1024,
		agent.Options.DebugLevel,
		logger.TargetTypeAgent,
	); err != nil {
		fmt.Fprintf(os.Stderr, "In %s(), failed to initialize logger [pid=%d]: %v", fn, os.Getpid(), err)
		os.Exit(1)
	}

	//catch runtime panic errors
	defer func() {
		if r := recover(); r != nil {
			//output stacktrace
			logger.WriteLog("JAAGENTJOBRUNCLI100001", string(debug.Stack()))
			os.Exit(1)
		}
	}()

	tmpFolderPath := agent.Options.TmpDir
	extJobPath = agent.Options.JaExtjobPath
	serverIPsPath := filepath.Join(agent.Options.TmpDir, common.AgentManagerFolder, "serverIPs", "serverIPs.json")
	common.LockFilePath = filepath.Join(agent.Options.TmpDir, "lock_file.lock")

	RebootFlagFilePrefix = filepath.Join(tmpFolderPath, "jobarg_agentd_reboot_flag-")
	RebootScriptFile = filepath.Join(extJobPath, RebootScriptFileName)
	dataFolderPath := filepath.Join(tmpFolderPath, common.AgentManagerFolder, "data")
	inFolderPath = filepath.Join(tmpFolderPath, common.AgentManagerFolder, "in")
	ExecFolderPath = filepath.Join(tmpFolderPath, common.AgentManagerFolder, "exec")
	endFolderPath := filepath.Join(tmpFolderPath, common.AgentManagerFolder, "end")
	jobsFolderPath := filepath.Join(tmpFolderPath, common.AgentManagerFolder, "jobs")

	logger.WriteLog("JAAGENTJOBRUNCLI400001", fn, *jobRunFilePath)

	// Create event file so that sender will send response to server
	bytes, err := os.ReadFile(*jobRunFilePath)
	if err != nil {
		logger.WriteLog("JAAGENTJOBRUNCLI200001", fn, *jobRunFilePath, err.Error())
		os.Exit(1)
	}

	logger.WriteLog("JAAGENTJOBRUNCLI400002", fn, *jobRunFilePath)

	// Parse the bytes
	eventData, _, err := agentutils.UnmarshalEventOrTCPMessage(bytes)
	if err != nil {
		logger.WriteLog("JAAGENTJOBRUNCLI200002", fn, *jobRunFilePath, string(bytes), err.Error())
		os.Exit(1)
	}

	logger.WriteLog("JAAGENTJOBRUNCLI400003", fn, eventData.Event.Name, eventData.TCPMessage.Kind, eventData.TCPMessage.Hostname, eventData.TCPMessage.Version, eventData.TCPMessage.ServerID)

	var tcpData common.JobResultData
	err = utils.Convert(eventData.TCPMessage.Data, &tcpData)

	if err != nil {
		logger.WriteLog("JAAGENTJOBRUNCLI200003", fn, utils.ToReadableString(eventData.TCPMessage.Data), err.Error())
		os.Exit(1)
	}

	logger.WriteLog("JAAGENTJOBRUNCLI400004", fn, tcpData.JobID, tcpData.Type, tcpData.Method, tcpData.Script)

	// Fork a new process to run the script file
	// Run the script file
	pid := os.Getpid()
	pidStr := strconv.Itoa(pid)
	baseName := strings.TrimSuffix(filepath.Base(*jobRunFilePath), ".json")
	scriptFileName := fmt.Sprintf("%s-%s.%s", baseName, pidStr, agentutils.GetScriptExtension())
	scriptFilePath := filepath.Join(dataFolderPath, scriptFileName)
	dataFileBaseName := agentutils.GetBasenameWithoutExt(scriptFilePath)
	jobFilePath := filepath.Join(ExecFolderPath, dataFileBaseName) + ".job"

	// Create job file under exec; write start time
	err = agentutils.WriteCurrentTimeToFile(jobFilePath)
	if err != nil {
		logger.WriteLog("JAAGENTJOBRUNCLI200014", fn, jobFilePath, tcpData.JobID, err.Error())
		os.Exit(1)
	}

	// Prepare job scripts for job types
	switch tcpData.Method {
	case common.AgentMethodNormal, common.AgentMethodAbort:
		// check job type
		switch tcpData.Type {
		case common.AgentJobTypeExt:
			// prepare the script command for extjob
			if err := prepareExtJobScript(&tcpData); err != nil {
				logger.WriteLog("JAAGENTJOBRUNCLI200005", fn, tcpData.JobID, err.Error())
				os.Exit(1)
			}
		case common.AgentJobTypeReboot:
			if err := prepareRebootJobScript(&tcpData); err != nil {
				logger.WriteLog("JAAGENTJOBRUNCLI200006", fn, tcpData.JobID, err.Error())
				os.Exit(1)
			}
		case common.AgentJobTypeCommand:
		}
	case common.AgentMethodTest:
		tcpData.Script = ""
	default:
		logger.WriteLog("JAAGENTJOBRUNCLI200004", fn, tcpData.JobID, tcpData.Type)
		os.Exit(1)
	}

	logger.WriteLog("JAAGENTJOBRUNCLI400010", fn, tcpData.JobID, tcpData.Script)

	var langExport string
	if agent.Options.Locale != "" {
		if runtime.GOOS != "windows" {
			// Linux locale string
			langExport = fmt.Sprintf(
				"export LANG=%s > /dev/null 2>&1 && export LC_ALL=%s > /dev/null 2>&1\n",
				agent.Options.Locale, agent.Options.Locale,
			)
		} else {
			// Windows code page
			langExport = fmt.Sprintf("chcp %s > NUL\n", agent.Options.Locale)
		}
	}

	err = agentutils.WriteFileAsLocale(scriptFilePath, langExport+tcpData.Script, agent.Options.Locale)
	if err != nil {
		logger.WriteLog("JAAGENTJOBRUNCLI200007", fn, scriptFilePath, tcpData.JobID, err.Error())
		os.Exit(1)
	}

	logger.WriteLog("JAAGENTJOBRUNCLI400011", fn, scriptFilePath, tcpData.JobID)

	if err := os.Chmod(scriptFilePath, 0755); err != nil {
		logger.WriteLog("JAAGENTJOBRUNCLI200008", fn, scriptFilePath, tcpData.JobID, err.Error())
		os.Exit(1)
	}

	// ENV
	envEncoded, err := agentutils.EncodeEnv(tcpData.Env, agent.Options.Locale)
	if err != nil {
		logger.WriteLog("JAAGENTJOBRUNCLI200009", fn, utils.ToReadableString(tcpData.Env), tcpData.JobID, err.Error())
		os.Exit(1)
	}

	logger.WriteLog("JAAGENTJOBRUNCLI400012", fn)

	env := os.Environ()
	env = append(env, envEncoded...)

	stdoutPath := filepath.Join(dataFolderPath, dataFileBaseName) + ".stdout"
	stderrPath := filepath.Join(dataFolderPath, dataFileBaseName) + ".stderr"
	retPath := filepath.Join(dataFolderPath, dataFileBaseName) + ".ret"

	var domain, username, password string
	// set username only when it is not a reboot job
	if tcpData.Type != common.AgentJobTypeReboot {
		domain, username, password = getDomainUsernamePassword(&tcpData)
	}

	logger.WriteLog("JAAGENTJOBRUNCLI400013", fn, stdoutPath, stderrPath, retPath, username, password, domain)

	if password != "" {
		if eventData.TCPMessage.EncryptVersion != common.EncryptVersion {
			// Custom decryption
			password = utils.DecryptLegacyJobString(password)
		} else {
			// AES decryption
			password, err = utils.DecryptPassword(password)
			if err != nil {
				logger.WriteLog("JAAGENTJOBRUNCLI200033", fn, tcpData.JobID, err)
				os.Exit(1)
			}
		}
	}

	procForker := forker.New(forker.ProcessData{
		ExecPath:   scriptFilePath,
		StdoutPath: stdoutPath,
		StderrPath: stderrPath,
		RetPath:    retPath,
		Username:   username,
		Password:   password,
		Domain:     domain,

		Detached:        false,
		UseConsoleStdin: true,
		Env:             env,
	})

	// Add start time
	startFilePath := filepath.Join(dataFolderPath, dataFileBaseName) + ".start"
	err = agentutils.CreateFile(startFilePath)
	if err != nil {
		logger.WriteLog("JAAGENTJOBRUNCLI200010", fn, startFilePath, tcpData.JobID, err.Error())
		os.Exit(1)
	}

	logger.WriteLog("JAAGENTJOBRUNCLI400014", fn, startFilePath, tcpData.JobID)

	endFilePath := filepath.Join(dataFolderPath, dataFileBaseName) + ".end"
	err = agentutils.CreateFile(endFilePath)
	if err != nil {
		logger.WriteLog("JAAGENTJOBRUNCLI200011", fn, endFilePath, tcpData.JobID, err.Error())
		os.Exit(1)
	}

	logger.WriteLog("JAAGENTJOBRUNCLI400015", fn, endFilePath, tcpData.JobID)

	cmd, err := procForker.StartNewProcess()
	if err != nil {
		tcpData.Result = common.JA_JOBRESULT_FAIL
		tcpData.JobStatus = common.AgentJobStatusEnd
		tcpData.Message = err.Error()
		tcpData.ReturnCode = -1

		logger.WriteLog("JAAGENTJOBRUNCLI200012", fn, tcpData.JobID, err.Error())

		err = agentutils.WriteToFile(*jobRunFilePath, eventData)
		if err != nil {
			logger.WriteLog("JAAGENTJOBRUNCLI200013", fn, *jobRunFilePath, tcpData.Result, tcpData.JobStatus, tcpData.JobID, err.Error())
			os.Exit(1)
		}

		logger.WriteLog("JAAGENTJOBRUNCLI400016", fn, *jobRunFilePath, tcpData.Result, tcpData.JobStatus, tcpData.JobID)

		// Add end time for .job file
		err = agentutils.WriteCurrentTimeToFile(jobFilePath)
		if err != nil {
			logger.WriteLog("JAAGENTJOBRUNCLI200014", fn, jobFilePath, tcpData.JobID, err.Error())
			os.Exit(1)
		}

		agentutils.PrepareJobResultHeader(eventData, &tcpData, pid)
		eventData.TCPMessage.Data = tcpData
		eventData.Transfer.Files = []common.FileTransfer{}

		// Move job to end
		if err := agentutils.CreateJobResultJsonFile(eventData, jobFilePath, *jobRunFilePath, inFolderPath, endFolderPath, tcpData.JobID); err != nil {
			logger.WriteLog("JAAGENTJOBRUNCLI200015", fn, tcpData.JobID, err.Error())
			os.Exit(1)
		}

		os.Exit(1)
	}

	err = agentutils.WriteCurrentTimeToFile(startFilePath)
	if err != nil {
		logger.WriteLog("JAAGENTJOBRUNCLI200016", fn, startFilePath, tcpData.JobID, err.Error())
		os.Exit(1)
	}

	logger.WriteLog("JAAGENTJOBRUNCLI400017", fn, startFilePath, tcpData.JobID)

	// Change status
	tcpData.Result = common.JA_JOBRESULT_SUCCEED
	tcpData.JobStatus = common.AgentJobStatusRun

	eventData.TCPMessage.Data = tcpData

	err = agentutils.WriteToFile(*jobRunFilePath, eventData)
	if err != nil {
		logger.WriteLog("JAAGENTJOBRUNCLI200013", fn, *jobRunFilePath, tcpData.Result, tcpData.JobStatus, tcpData.JobID, err.Error())
		os.Exit(1)
	}

	logger.WriteLog("JAAGENTJOBRUNCLI400016", fn, *jobRunFilePath, tcpData.Result, tcpData.JobStatus, tcpData.JobID)

	if err := cmd.Wait(); err != nil {
		exitErr, ok := err.(*exec.ExitError)
		if !ok {
			// Something went wrong in starting or waiting for the process itself
			logger.WriteLog("JAAGENTJOBRUNCLI200017", fn, tcpData.JobID, err.Error())
			os.Exit(1)
		}

		// The process ran but exited with a non-zero code
		code := exitErr.ExitCode()

		// Convert to signed number
		signedCode := int32(uint32(code))

		logger.WriteLog("JAAGENTJOBRUNCLI400018", fn, signedCode, tcpData.JobID)
		err = agentutils.WriteStringToFile(retPath, fmt.Sprintf("%d", signedCode))
		if err != nil {
			logger.WriteLog("JAAGENTJOBRUNCLI200027", fn, signedCode, retPath, err.Error())
			os.Exit(1)
		}
	}

	// return for reboot process
	if tcpData.Type == common.AgentJobTypeReboot {
		logger.WriteLog("JAAGENTJOBRUNCLI400019", fn, tcpData.JobID)
		os.Exit(0)
	}

	// Add end time
	err = agentutils.WriteCurrentTimeToFile(endFilePath)
	if err != nil {
		logger.WriteLog("JAAGENTJOBRUNCLI200018", fn, endFilePath, tcpData.JobID, err.Error())
		os.Exit(1)
	}

	logger.WriteLog("JAAGENTJOBRUNCLI400020", fn, endFilePath, tcpData.JobID)

	// Add end time for .job file
	err = agentutils.WriteCurrentTimeToFile(jobFilePath)
	if err != nil {
		logger.WriteLog("JAAGENTJOBRUNCLI200014", fn, jobFilePath, tcpData.JobID, err.Error())
		os.Exit(1)
	}

	logger.WriteLog("JAAGENTJOBRUNCLI400021", fn, endFilePath, tcpData.JobID)

	agentutils.PrepareJobResultHeader(eventData, &tcpData, pid)

	serverIP, err := agentutils.GetIPByServerID(serverIPsPath, eventData.TCPMessage.ServerID)
	if err != nil {
		logger.WriteLog("JAAGENTJOBRUNCLI200019", fn, eventData.TCPMessage.ServerID, tcpData.JobID, err.Error())
		os.Exit(1)
	}

	tmpJobFilePath := filepath.Join(jobsFolderPath, fmt.Sprintf("%s_%s", tcpData.JobID, strings.ReplaceAll(serverIP, ".", "-")+".job"))
	tcpData.PreUniqueID, err = agentutils.ReadLastLineOrNew(tmpJobFilePath)
	if err != nil {
		logger.WriteLog("JAAGENTJOBRUNCLI200020", fn, tmpJobFilePath, tcpData.JobID, err.Error())
		os.Exit(1)
	}

	logger.WriteLog("JAAGENTJOBRUNCLI400022", fn, tmpJobFilePath, tcpData.JobID, tcpData.PreUniqueID)

	tcpData.CurUniqueID = fmt.Sprintf("%s_%s_%s", tcpData.JobID, agentutils.GetTimestampWithNanoseconds(), strings.ReplaceAll(serverIP, ".", "-"))
	logger.WriteLog("JAAGENTJOBRUNCLI400023", fn, tcpData.CurUniqueID, tcpData.PreUniqueID, tcpData.JobID)

	// Read stdout
	content, err := agentutils.ReadFileAsLocale(stdoutPath, agent.Options.Locale)
	if err != nil {
		logger.WriteLog("JAAGENTJOBRUNCLI200021", fn, stdoutPath, tcpData.JobID, err.Error())
		os.Exit(1)
	}

	tcpData.StdOut = string(content)
	logger.WriteLog("JAAGENTJOBRUNCLI400024", fn, stdoutPath, tcpData.JobID, tcpData.StdOut)

	// Read stderr
	content, err = agentutils.ReadFileAsLocale(stderrPath, agent.Options.Locale)
	if err != nil {
		logger.WriteLog("JAAGENTJOBRUNCLI200022", fn, stderrPath, tcpData.JobID, err.Error())
		os.Exit(1)
	}

	tcpData.StdErr = string(content)
	logger.WriteLog("JAAGENTJOBRUNCLI400025", fn, stderrPath, tcpData.JobID, tcpData.StdErr)

	// Read return code
	content, err = agentutils.ReadFileAsLocale(retPath, agent.Options.Locale)
	if err != nil {
		logger.WriteLog("JAAGENTJOBRUNCLI200023", fn, retPath, tcpData.JobID, err.Error())
		os.Exit(1)
	}

	retCodeStr := string(content)
	logger.WriteLog("JAAGENTJOBRUNCLI400026", fn, retPath, tcpData.JobID, retCodeStr)

	var retCode int

	if retCodeStr != "" {
		retCode, err = strconv.Atoi(strings.TrimSpace(retCodeStr))
		if err != nil {
			logger.WriteLog("JAAGENTJOBRUNCLI200024", fn, retCodeStr, tcpData.JobID, err.Error())
			os.Exit(1)
		}

		// Windows special handling (int -> unsigned long)
		if runtime.GOOS == "windows" && agent.Options.ExtUnsignedFlag != 0 {
			retCode = int(uint32(retCode))
		}

		// Convert back to string
		retCodeStr = strconv.Itoa(retCode)
	}

	// Write exit code to job file
	if err := agentutils.WriteStringToFile(jobFilePath, retCodeStr); err != nil {
		logger.WriteLog("JAAGENTJOBRUNCLI200025", fn, retCodeStr, jobFilePath, tcpData.JobID, err.Error())
		os.Exit(1)
	}

	tcpData.ReturnCode = retCode
	tcpData.JobStatus = common.AgentJobStatusEnd
	tcpData.Result = common.JA_JOBRESULT_SUCCEED
	eventData.TCPMessage.Data = tcpData

	eventData.Transfer.Files = []common.FileTransfer{}

	if err := agentutils.CreateJobResultJsonFile(eventData, jobFilePath, *jobRunFilePath, inFolderPath, endFolderPath, tcpData.JobID); err != nil {
		logger.WriteLog("JAAGENTJOBRUNCLI200015", fn, tcpData.JobID, err.Error())
		os.Exit(1)
	}

}

func getDomainUsernamePassword(tcpData *common.JobResultData) (string, string, string) {
	var domain, username, password string

	if tcpData.RunUser != nil {
		username = *tcpData.RunUser
	}
	if tcpData.RunUserPassword != nil {
		password = *tcpData.RunUserPassword
	}

	if username == "" {
		username = agent.Options.JaCommandUser
		password = agent.Options.JaCommandPassword
	}

	if i := strings.Index(username, `\`); i >= 0 {
		domain = username[:i]
		username = username[i+1:]
	}

	return domain, username, password
}

func prepareRebootJobScript(tcpData *common.JobResultData) error {
	fn := "prepareRebootScript"

	// Get reboot args
	argument, ok := tcpData.Argument.(map[string]any)
	if !ok {
		return fmt.Errorf("malformed argument: %v", tcpData.Argument)
	}

	logger.WriteLog("JAAGENTJOBRUNCLI400006", fn, utils.ToReadableString(argument), tcpData.JobID)
	// parse values from argument
	rebootModeFlagAny, ok := argument["reboot_mode_flag"]
	if !ok {
		return fmt.Errorf("cannot get reboot_mode_flag from argument: %v", argument)
	}

	rebootWaitTimeAny, ok := argument["reboot_wait_time"]
	if !ok {
		return fmt.Errorf("cannot get reboot_wait_time from argument: %v", argument)
	}

	rebootModeFlag, err := agentutils.ParseAnyToInt(rebootModeFlagAny)
	if err != nil {
		return fmt.Errorf("rebootModeFlag conversion failed: %v", err)
	}
	rebootWaitTime, err := agentutils.ParseAnyToInt(rebootWaitTimeAny)
	if err != nil {
		return fmt.Errorf("rebootWaitTime conversion failed: %v", err)
	}

	// create reboot flag file: prefix + jobid
	rebootFlagFilePath := fmt.Sprintf("%s%s", RebootFlagFilePrefix, tcpData.JobID)
	if err := agentutils.CreateFile(rebootFlagFilePath); err != nil {
		return fmt.Errorf("error creating reboot flag file '%s': %v", rebootFlagFilePath, err)
	}

	logger.WriteLog("JAAGENTJOBRUNCLI400008", fn, rebootFlagFilePath, tcpData.JobID)

	// Set reboot script
	tcpData.Script = fmt.Sprintf(`"%s" "%s"`, RebootScriptFile, rebootFlagFilePath)

	logger.WriteLog("JAAGENTJOBRUNCLI400009", fn, tcpData.Script, tcpData.JobID)

	go func() {
		if err := agentutils.WaitJobCompleteAndDeleteFlag(ExecFolderPath, rebootFlagFilePath, rebootWaitTime, rebootModeFlag); err != nil {
			logger.WriteLog("JAAGENTJOBRUNCLI200026", fn, rebootFlagFilePath, tcpData.JobID, err.Error())
		}
	}()

	return nil

}

func prepareExtJobScript(tcpData *common.JobResultData) error {

	fn := "prepareExtJobScript"

	argument, err := castToStringSlice(tcpData.Argument)

	if err != nil {
		return fmt.Errorf("invalid argument: %v", err)
	}

	if len(argument) <= 0 {
		return fmt.Errorf("job argument is missing")
	}

	logger.WriteLog("JAAGENTJOBRUNCLI400006", fn, argument, tcpData.JobID)

	jobScript := filepath.Join(extJobPath, fmt.Sprintf("%s.%s", tcpData.Script, agentutils.GetScriptExtension()))

	var quoted []string
	for _, arg := range argument {
		quoted = append(quoted, quoteArg(arg))
	}

	quotedArgs := strings.Join(quoted, " ")
	tcpData.Script = fmt.Sprintf(`"%s" %s`, jobScript, quotedArgs)

	logger.WriteLog("JAAGENTJOBRUNCLI400007", fn, tcpData.Script, tcpData.JobID)
	return nil
}

func quoteArg(arg string) string {
	if strings.ContainsAny(arg, " \t\"") {
		// Surround with quotes and escape embedded quotes
		return `"` + strings.ReplaceAll(arg, `"`, `\"`) + `"`
	}
	return arg
}

func castToStringSlice(value any) ([]string, error) {
	rawSlice, ok := value.([]any)
	if !ok {
		return nil, fmt.Errorf("value is not a slice: %T", value)
	}

	strSlice := make([]string, len(rawSlice))
	for i, v := range rawSlice {
		s, ok := v.(string)
		if !ok {
			return nil, fmt.Errorf("element at index %d is not a string: %T", i, v)
		}
		strSlice[i] = s
	}
	return strSlice, nil
}
