package process

import (
	"fmt"
	"log"
	"os"
	"os/exec"
	"sync"
	"syscall"
	"time"
)

// GameServer manages the game server process
type GameServer struct {
	path       string
	args       string
	cmd        *exec.Cmd
	mu         sync.RWMutex
	startTime  time.Time
	isRunning  bool
	lastStatus string
}

// NewGameServer creates a new game server manager
func NewGameServer(path, args string) *GameServer {
	return &GameServer{
		path:       path,
		args:       args,
		lastStatus: "stopped",
	}
}

// Start starts the game server process
func (gs *GameServer) Start() error {
	gs.mu.Lock()
	defer gs.mu.Unlock()

	if gs.isRunning {
		return fmt.Errorf("server is already running")
	}

	// Check if executable exists
	if _, err := os.Stat(gs.path); os.IsNotExist(err) {
		return fmt.Errorf("server executable not found: %s", gs.path)
	}

	log.Printf("Starting game server: %s %s", gs.path, gs.args)

	// Create command
	gs.cmd = exec.Command(gs.path)

	// Parse args if provided
	if gs.args != "" {
		// Simple space-split parsing (TODO: handle quoted args properly)
		gs.cmd.Args = append(gs.cmd.Args, splitArgs(gs.args)...)
	}

	// Set working directory to server directory
	gs.cmd.Dir = getDirectory(gs.path)

	// Redirect output to our logs
	gs.cmd.Stdout = os.Stdout
	gs.cmd.Stderr = os.Stderr

	// Start the process
	if err := gs.cmd.Start(); err != nil {
		gs.isRunning = false
		gs.lastStatus = "failed"
		return fmt.Errorf("failed to start server: %w", err)
	}

	gs.isRunning = true
	gs.startTime = time.Now()
	gs.lastStatus = "running"

	// Monitor process in background to prevent zombies
	go gs.monitorProcess()

	log.Printf("Game server started with PID %d", gs.cmd.Process.Pid)
	return nil
}

// Stop stops the game server process
func (gs *GameServer) Stop() error {
	gs.mu.Lock()
	defer gs.mu.Unlock()

	if !gs.isRunning || gs.cmd == nil || gs.cmd.Process == nil {
		return fmt.Errorf("server is not running")
	}

	log.Printf("Stopping game server (PID %d)", gs.cmd.Process.Pid)

	// Send SIGTERM for graceful shutdown
	if err := gs.cmd.Process.Signal(syscall.SIGTERM); err != nil {
		log.Printf("Failed to send SIGTERM, forcing kill: %v", err)
		// Force kill if SIGTERM fails
		if killErr := gs.cmd.Process.Kill(); killErr != nil {
			return fmt.Errorf("failed to kill process: %w", killErr)
		}
	}

	// Wait for process to exit (with timeout)
	done := make(chan error, 1)
	go func() {
		done <- gs.cmd.Wait()
	}()

	select {
	case <-done:
		log.Println("Server stopped gracefully")
	case <-time.After(30 * time.Second):
		log.Println("Server did not stop gracefully, forcing kill")
		gs.cmd.Process.Kill()
		<-done
	}

	gs.isRunning = false
	gs.lastStatus = "stopped"
	gs.cmd = nil

	return nil
}

// Restart restarts the game server
func (gs *GameServer) Restart() error {
	log.Println("Restarting game server...")

	// Stop if running
	if gs.isRunning {
		if err := gs.Stop(); err != nil {
			return fmt.Errorf("failed to stop server for restart: %w", err)
		}
	}

	// Wait a moment for cleanup
	time.Sleep(2 * time.Second)

	// Start again
	return gs.Start()
}

// Status returns the current server status
func (gs *GameServer) Status() string {
	gs.mu.RLock()
	defer gs.mu.RUnlock()

	if !gs.isRunning {
		return "stopped"
	}

	// Check if process is actually alive
	if gs.cmd != nil && gs.cmd.Process != nil {
		// Send signal 0 to check if process exists
		if err := gs.cmd.Process.Signal(syscall.Signal(0)); err != nil {
			return "crashed"
		}
	}

	return gs.lastStatus
}

// Uptime returns how long the server has been running
func (gs *GameServer) Uptime() time.Duration {
	gs.mu.RLock()
	defer gs.mu.RUnlock()

	if !gs.isRunning {
		return 0
	}

	return time.Since(gs.startTime)
}

// IsRunning returns whether the server is currently running
func (gs *GameServer) IsRunning() bool {
	gs.mu.RLock()
	defer gs.mu.RUnlock()
	return gs.isRunning
}

// monitorProcess waits for the process to exit and updates state
// This prevents zombie processes by calling Wait()
func (gs *GameServer) monitorProcess() {
	if gs.cmd == nil || gs.cmd.Process == nil {
		return
	}

	// Wait for process to exit (blocks until process dies)
	err := gs.cmd.Wait()

	gs.mu.Lock()
	defer gs.mu.Unlock()

	gs.isRunning = false

	if err != nil {
		log.Printf("Game server process exited with error: %v", err)
		gs.lastStatus = "crashed"
	} else {
		log.Println("Game server process exited normally")
		gs.lastStatus = "stopped"
	}

	// TODO: Could trigger crash recovery notification here
}

// Helper functions

func getDirectory(path string) string {
	for i := len(path) - 1; i >= 0; i-- {
		if path[i] == '/' || path[i] == '\\' {
			return path[:i]
		}
	}
	return "."
}

func splitArgs(args string) []string {
	// Simple space-based splitting
	// TODO: Handle quoted strings properly for args with spaces
	var result []string
	current := ""

	for _, char := range args {
		if char == ' ' {
			if current != "" {
				result = append(result, current)
				current = ""
			}
		} else {
			current += string(char)
		}
	}

	if current != "" {
		result = append(result, current)
	}

	return result
}
