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

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"time"

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

// MoveToSubFolder moves a file from its current folder to a target subfolder at the same level.
//
// It assumes the given full file path points to a file inside a directory structure,
// and it will place that file in a new sibling folder (`to`) under the grandparent directory.
//
// For example, if the file is located at:
//
//	/opt/data/job_manager/in/file.json
//
// and you call:
//
//	MoveToSubFolder("/opt/data/job_manager/in/file.json", "end")
//
// The file will be moved to:
//
//	/opt/data/job_manager/end/file.json
//
// This function is useful when organizing files by state, such as moving processed
// files from an "in" folder to an "end" folder, without changing the base path structure.
//
// Requirements:
//   - `fullFilePath` must be a valid absolute path to a file
//   - The `to` folder will be created if it doesn't exist
//
// Example:
//
//	err := MoveToSubFolder("/opt/data/job_manager/in/file.json", "end")
//	if err != nil {
//	    log.Fatalf("Move failed: %v", err)
//	}
func MoveToSubFolder(fullFilePath string, to string) error {
	functionName := "MoveToSubFolder"
	// Split the path into parts
	parts := strings.Split(filepath.Clean(fullFilePath), string(filepath.Separator))
	if len(parts) < 2 {
		return fmt.Errorf("invalid file path: %s, expected file name with parent folder", fullFilePath)
	}

	basePath := filepath.Join(parts[:len(parts)-2]...) //get the last folder of the full file path
	toFolder := filepath.Join(string(filepath.Separator), basePath, to)
	// Ensure destination directory exists
	if err := os.MkdirAll(toFolder, common.FilePermission); err != nil {
		return fmt.Errorf("failed to create target directory %s, err: [%w]", toFolder, err)
	}

	fileName := parts[len(parts)-1] // get the file name
	newFilePath := filepath.Join(string(filepath.Separator), basePath, to, fileName)

	// Acquire a shared lock on the lock file before moving the file.
	file, err := LockFile(LOCKFILE_SHARE_LOCK | LOCKFILE_FAIL_ON_LOCK)
	if err != nil {
		return fmt.Errorf("failed to acquire share lock. from dir: %s, to dir: %s, err: [%w]", fullFilePath, newFilePath, err)
	}

	defer func() {
		// Unlock the lock file
		err = UnlockFile(file)
		if err != nil {
			logger.JaLog("JAUTILS200002", logger.Logging{}, functionName, common.Manager.Name, fullFilePath, toFolder, err)
		}
	}()

	if err := os.Rename(fullFilePath, newFilePath); err != nil {
		return fmt.Errorf("failed to rename file: %s → %s. err: [%v]", fullFilePath, newFilePath, err)
	}

	logger.JaLog("JAUTILS400004", logger.Logging{}, functionName, fullFilePath, toFolder)

	return nil
}

func MoveAllFiles(fromFolder, toFolder string) error {
	funcName := "MoveAllFiles"
	if err := os.MkdirAll(toFolder, common.FilePermission); err != nil {
		return fmt.Errorf("failed to create destination directory: %w", err)
	}
	for {
		entries, err := os.ReadDir(fromFolder)
		if err != nil {
			return fmt.Errorf("failed to read source directory: %w", err)
		}
		if len(entries) == 0 {
			break
		}
		for _, entry := range entries {
			if entry.IsDir() {
				continue
			}
			fromPath := filepath.Join(fromFolder, entry.Name())
			toPath := filepath.Join(toFolder, entry.Name())
			if err := os.Rename(fromPath, toPath); err != nil {
				return fmt.Errorf("failed to move file: %s → %s: err: [%w]", fromPath, toPath, err)
			}
			logger.JaLog("JAUTILS400004", logger.Logging{}, funcName, fromPath, toFolder)
		}
	}
	return nil
}

func TouchFile(path string) error {
	_, err := os.Stat(path)
	if err == nil {
		now := time.Now()
		err = os.Chtimes(path, now, now)
		if err != nil {
			return err
		}
	}
	return err
}
