// Copyright 2025, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0

package shellutil

import (
	"log"
	"regexp"
)

const (
	MaxQuoteSize = 10000000 // 10MB
)

var (
	safePattern       = regexp.MustCompile(`^[a-zA-Z0-9_/.-]+$`)
	envVarNamePattern = regexp.MustCompile(`^[A-Za-z_][A-Za-z0-9_]*$`)
)

func IsValidEnvVarName(name string) bool {
	return envVarNamePattern.MatchString(name)
}

func HardQuote(s string) string {
	if s == "" {
		return "\"\""
	}

	if safePattern.MatchString(s) {
		return s
	}

	if !checkQuoteSize(s) {
		return ""
	}

	buf := make([]byte, 0, len(s)+5)
	buf = append(buf, '"')

	for i := 0; i < len(s); i++ {
		switch s[i] {
		case '"', '\\', '$', '`':
			buf = append(buf, '\\', s[i])
		case '\n':
			buf = append(buf, '\\', '\n')
		default:
			buf = append(buf, s[i])
		}
	}

	buf = append(buf, '"')
	return string(buf)
}

// does not encode newlines or backticks
func HardQuoteFish(s string) string {
	if s == "" {
		return "\"\""
	}

	if safePattern.MatchString(s) {
		return s
	}

	if !checkQuoteSize(s) {
		return ""
	}

	buf := make([]byte, 0, len(s)+5)
	buf = append(buf, '"')

	for i := 0; i < len(s); i++ {
		switch s[i] {
		case '"', '\\', '$':
			buf = append(buf, '\\', s[i])
		default:
			buf = append(buf, s[i])
		}
	}

	buf = append(buf, '"')
	return string(buf)
}

func HardQuotePowerShell(s string) string {
	if s == "" {
		return "\"\""
	}

	if !checkQuoteSize(s) {
		return ""
	}

	buf := make([]byte, 0, len(s)+5)
	buf = append(buf, '"')

	for i := 0; i < len(s); i++ {
		c := s[i]
		// In PowerShell, backtick (`) is the escape character
		switch c {
		case '"', '`', '$':
			buf = append(buf, '`')
		case '\n':
			buf = append(buf, '`', 'n') // PowerShell uses `n for newline
		}
		buf = append(buf, c)
	}

	buf = append(buf, '"')
	return string(buf)
}

func SoftQuote(s string) string {
	if s == "" {
		return "\"\""
	}

	// Handle special case of ~ paths
	if len(s) > 0 && s[0] == '~' {
		// If it's just ~ or ~/something with no special chars, leave it as is
		if len(s) == 1 || (len(s) > 1 && s[1] == '/' && safePattern.MatchString(s[2:])) {
			return s
		}

		// Otherwise quote everything after the ~ (including the /)
		if len(s) > 1 && s[1] == '/' {
			return "~" + SoftQuote(s[1:])
		}
	}

	if safePattern.MatchString(s) {
		return s
	}

	if !checkQuoteSize(s) {
		return ""
	}

	buf := make([]byte, 0, len(s)+5)
	buf = append(buf, '"')

	for i := 0; i < len(s); i++ {
		c := s[i]
		// In soft quote, we don't escape $ to allow expansion
		if c == '"' || c == '\\' || c == '`' {
			buf = append(buf, '\\')
		}
		buf = append(buf, c)
	}

	buf = append(buf, '"')
	return string(buf)
}

func checkQuoteSize(s string) bool {
	if len(s) > MaxQuoteSize {
		log.Printf("string too long to quote: %s", s)
		return false
	}
	return true
}
