package utils

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

	"github.com/0xJacky/Nginx-UI/internal/cache"
	"github.com/0xJacky/Nginx-UI/internal/helper"
	"github.com/0xJacky/Nginx-UI/internal/nginx"
	"github.com/0xJacky/Nginx-UI/settings"
)

// IsValidLogPath checks if a log path is valid:
// 1. It must be a regular file or a symlink to a regular file
// 2. It must not point to a console or special device
// 3. It must be under the whitelist directories
func IsValidLogPath(logPath string) bool {

	// First check if the path is in the whitelist
	if !isLogPathUnderWhiteList(logPath) {
		return false
	}

	// Check if the path exists
	fileInfo, err := os.Lstat(logPath)
	if err != nil {
		// If the file doesn't exist, it might be created later
		// We'll assume it's valid for now
		return true
	}

	// If it's a symlink, follow it safely
	if fileInfo.Mode()&os.ModeSymlink != 0 {
		// Use EvalSymlinks to safely resolve the entire symlink chain
		// This function detects circular symlinks and returns an error
		resolvedPath, err := filepath.EvalSymlinks(logPath)
		if err != nil {
			return false
		}

		if !isLogPathUnderWhiteList(resolvedPath) {
			return false
		}

		// Check the resolved target file
		targetInfo, err := os.Stat(resolvedPath)
		if err != nil {
			return false
		}

		// Only accept regular files as targets
		result := targetInfo.Mode().IsRegular()
		return result
	}

	// For non-symlinks, just check if it's a regular file
	result := fileInfo.Mode().IsRegular()
	return result
}

// isLogPathUnderWhiteList checks if a log path is under one of the paths in LogDirWhiteList
func isLogPathUnderWhiteList(path string) bool {
	prefix := nginx.GetPrefix()
	cacheKey := fmt.Sprintf("isLogPathUnderWhiteList:%s", path)
	res, ok := cache.Get(cacheKey)

	// If cached, return the result directly
	if ok {
		return res.(bool)
	}

	// Only build the whitelist when cache miss occurs
	logDirWhiteList := append([]string{}, settings.NginxSettings.LogDirWhiteList...)

	accessLogPath := nginx.GetAccessLogPath()
	errorLogPath := nginx.GetErrorLogPath()

	if accessLogPath != "" {
		logDirWhiteList = append(logDirWhiteList, filepath.Dir(accessLogPath))
	}
	if errorLogPath != "" {
		logDirWhiteList = append(logDirWhiteList, filepath.Dir(errorLogPath))
	}
	if prefix != "" {
		logDirWhiteList = append(logDirWhiteList, prefix)
	}

	// Check if path is under any whitelist directory
	for _, whitePath := range logDirWhiteList {
		if helper.IsUnderDirectory(path, whitePath) {
			cache.Set(cacheKey, true, 0)
			return true
		}
	}

	// Cache negative result as well to avoid repeated checks
	cache.Set(cacheKey, false, 0)
	return false
}
