// Symbolic Evaluation Test File - Go
//
// This file contains multiple source→sink flows for testing symbolic evaluation.
// Each section demonstrates dangerous (unsafe) and safe patterns.
//
// Patterns covered:
// - SQL injection (fmt.Sprintf vs parameterized queries)
// - Command injection (exec.Command)
// - Path traversal (os.ReadFile, filepath.Join)
// - net/http request handling
// - Error handling patterns
// - Goroutines and channels

package symbolic

import (
	"database/sql"
	"fmt"
	"io"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
)

// =============================================================================
// DATA MODELS
// =============================================================================

type User struct {
	ID       string
	Username string
	Email    string
}

// =============================================================================
// SQL INJECTION PATTERNS
// =============================================================================

type UserRepository struct {
	db *sql.DB
}

func NewUserRepository(db *sql.DB) *UserRepository {
	return &UserRepository{db: db}
}

// UNSAFE: fmt.Sprintf with user input
func (r *UserRepository) GetUserUnsafeSprintf(userID string) (*User, error) {
	query := fmt.Sprintf("SELECT * FROM users WHERE id = '%s'", userID)
	row := r.db.QueryRow(query) // Sink: danger
	return r.scanUser(row)
}

// UNSAFE: String concatenation
func (r *UserRepository) GetUserUnsafeConcat(username string) (*User, error) {
	query := "SELECT * FROM users WHERE username = '" + username + "'"
	row := r.db.QueryRow(query) // Sink: danger
	return r.scanUser(row)
}

// UNSAFE: fmt.Sprintf with %v
func (r *UserRepository) GetUserUnsafeFormatV(email string) (*User, error) {
	query := fmt.Sprintf("SELECT * FROM users WHERE email = '%v'", email)
	row := r.db.QueryRow(query) // Sink: danger
	return r.scanUser(row)
}

// SAFE: Parameterized query
func (r *UserRepository) GetUserSafeParameterized(userID string) (*User, error) {
	query := "SELECT * FROM users WHERE id = ?"
	row := r.db.QueryRow(query, userID) // Safe: parameterized
	return r.scanUser(row)
}

// SAFE: Constant query
func (r *UserRepository) GetAllUsersSafe() ([]*User, error) {
	query := "SELECT * FROM users"
	rows, err := r.db.Query(query) // Safe: constant
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	return r.scanUsers(rows)
}

func (r *UserRepository) scanUser(row *sql.Row) (*User, error) {
	user := &User{}
	err := row.Scan(&user.ID, &user.Username, &user.Email)
	if err != nil {
		return nil, err
	}
	return user, nil
}

func (r *UserRepository) scanUsers(rows *sql.Rows) ([]*User, error) {
	var users []*User
	for rows.Next() {
		user := &User{}
		if err := rows.Scan(&user.ID, &user.Username, &user.Email); err != nil {
			return nil, err
		}
		users = append(users, user)
	}
	return users, nil
}

// =============================================================================
// COMMAND INJECTION PATTERNS
// =============================================================================

type CommandService struct{}

// UNSAFE: exec.Command with shell
func (s *CommandService) ExecuteUnsafeShell(filename string) (string, error) {
	command := "cat " + filename
	cmd := exec.Command("sh", "-c", command) // Sink: danger
	output, err := cmd.Output()
	return string(output), err
}

// UNSAFE: fmt.Sprintf in command
func (s *CommandService) ExecuteUnsafeSprintf(path string) (string, error) {
	command := fmt.Sprintf("ls %s", path)
	cmd := exec.Command("sh", "-c", command) // Sink: danger
	output, err := cmd.Output()
	return string(output), err
}

// SAFE: exec.Command with separate arguments
func (s *CommandService) ExecuteSafeArgs(filename string) (string, error) {
	cmd := exec.Command("cat", filename) // Safer: no shell
	output, err := cmd.Output()
	return string(output), err
}

// SAFE: Constant command
func (s *CommandService) ListDirectorySafe() (string, error) {
	cmd := exec.Command("ls", "-la") // Safe: constant
	output, err := cmd.Output()
	return string(output), err
}

// =============================================================================
// FILE OPERATIONS PATTERNS
// =============================================================================

type FileService struct {
	basePath string
}

func NewFileService(basePath string) *FileService {
	return &FileService{basePath: basePath}
}

// UNSAFE: String concatenation path
func (s *FileService) ReadFileUnsafeConcat(filename string) ([]byte, error) {
	path := s.basePath + "/" + filename
	return os.ReadFile(path) // Sink: path traversal
}

// UNSAFE: fmt.Sprintf path
func (s *FileService) ReadFileUnsafeSprintf(filename string) ([]byte, error) {
	path := fmt.Sprintf("%s/%s", s.basePath, filename)
	return os.ReadFile(path) // Sink: danger
}

// UNSAFE: filepath.Join still allows traversal
func (s *FileService) ReadFileUnsafeJoin(filename string) ([]byte, error) {
	path := filepath.Join(s.basePath, filename)
	return os.ReadFile(path) // Sink: danger (Join doesn't prevent ..)
}

// SAFE: Validated path
func (s *FileService) ReadFileSafe(filename string) ([]byte, error) {
	if filepath.IsAbs(filename) || containsDotDot(filename) {
		return nil, fmt.Errorf("invalid filename")
	}
	path := filepath.Join(s.basePath, filename)
	// Verify resolved path is still under basePath
	absPath, _ := filepath.Abs(path)
	absBase, _ := filepath.Abs(s.basePath)
	if len(absPath) < len(absBase) {
		return nil, fmt.Errorf("path traversal detected")
	}
	return os.ReadFile(path) // Safe after validation
}

func containsDotDot(path string) bool {
	return filepath.Clean(path) != path || path == ".."
}

// =============================================================================
// VARIABLE PROPAGATION PATTERNS
// =============================================================================

type DataProcessor struct{}

// UNSAFE: Assignment propagates danger
func (p *DataProcessor) ProcessUnsafe(input string) string {
	processed := input                           // Assignment propagates
	transformed := fmt.Sprintf("%s", processed)  // Still dangerous
	return transformed
}

// UNSAFE: Multiple assignments
func (p *DataProcessor) ChainAssignmentUnsafe(value string) string {
	a := value
	b := a
	c := b
	return c // Danger propagates
}

// SAFE: Reassignment to constant
func (p *DataProcessor) ProcessSafe(input string) string {
	processed := input
	processed = "constant_value" // Reassignment breaks danger
	return processed
}

// =============================================================================
// ERROR HANDLING PATTERNS
// =============================================================================

type ErrorHandlingService struct{}

// UNSAFE: Error message with user input
func (s *ErrorHandlingService) HandleErrorUnsafe(userInput string) error {
	return fmt.Errorf("invalid input: %s", userInput) // Danger in error
}

// UNSAFE: Wrapped error with danger
func (s *ErrorHandlingService) WrapErrorUnsafe(input string, err error) error {
	return fmt.Errorf("processing %s: %w", input, err) // Danger propagates
}

// SAFE: Constant error message
func (s *ErrorHandlingService) HandleErrorSafe() error {
	return fmt.Errorf("invalid input provided") // Safe: constant
}

// =============================================================================
// HTTP HANDLERS (Sources)
// =============================================================================

func SetupRoutes(repo *UserRepository, cmdSvc *CommandService, fileSvc *FileService) {
	// UNSAFE: Query parameter to SQL
	http.HandleFunc("/user/unsafe", func(w http.ResponseWriter, r *http.Request) {
		userID := r.URL.Query().Get("id") // Source: user input
		user, _ := repo.GetUserUnsafeSprintf(userID) // Danger
		fmt.Fprintf(w, "%+v", user)
	})

	// SAFE: Parameterized query
	http.HandleFunc("/user/safe", func(w http.ResponseWriter, r *http.Request) {
		userID := r.URL.Query().Get("id") // Source: user input
		user, _ := repo.GetUserSafeParameterized(userID) // Safe
		fmt.Fprintf(w, "%+v", user)
	})

	// UNSAFE: Form value to command
	http.HandleFunc("/exec/unsafe", func(w http.ResponseWriter, r *http.Request) {
		filename := r.FormValue("filename") // Source: user input
		result, _ := cmdSvc.ExecuteUnsafeShell(filename) // Danger
		io.WriteString(w, result)
	})

	// UNSAFE: Path parameter to file read
	http.HandleFunc("/file/unsafe/", func(w http.ResponseWriter, r *http.Request) {
		filename := r.URL.Path[len("/file/unsafe/"):] // Source: user input
		content, _ := fileSvc.ReadFileUnsafeConcat(filename) // Danger
		w.Write(content)
	})
}

// =============================================================================
// CONTROL FLOW PATTERNS
// =============================================================================

type ControlFlowService struct{}

// UNSAFE: If statement with danger
func (s *ControlFlowService) ConditionalUnsafe(userInput string) string {
	var result string
	if len(userInput) > 0 {
		result = userInput // Danger in true branch
	} else {
		result = "default"
	}
	return result
}

// UNSAFE: Switch with danger
func (s *ControlFlowService) SwitchUnsafe(userInput string, code int) string {
	switch code {
	case 1:
		return userInput // Danger
	case 2:
		return "option_b"
	default:
		return "default"
	}
}

// SAFE: Both branches are constants
func (s *ControlFlowService) ConditionalSafe(flag bool) string {
	if flag {
		return "option_a"
	}
	return "option_b"
}

// =============================================================================
// GOROUTINE PATTERNS
// =============================================================================

type ConcurrencyService struct{}

// UNSAFE: Channel receives dangerous data
func (s *ConcurrencyService) ProcessChannelUnsafe(input string) string {
	ch := make(chan string, 1)
	go func() {
		ch <- input // Danger sent to channel
	}()
	return <-ch // Danger received from channel
}

// SAFE: Channel with constant
func (s *ConcurrencyService) ProcessChannelSafe() string {
	ch := make(chan string, 1)
	go func() {
		ch <- "constant" // Safe
	}()
	return <-ch
}
