package system

import (
    "fmt"
    "io"
    "os"
    "os/exec"
    "path/filepath"
    "strings"       // <-- ADD THIS
	"sync"
    "time"

    "zlh-agent/internal/provision"
    "zlh-agent/internal/state"
)


/* --------------------------------------------------------------------------
   GLOBAL PROCESS STATE
----------------------------------------------------------------------------*/

var (
	mu          sync.Mutex
	serverCmd   *exec.Cmd
	serverStdin io.WriteCloser
)

/* --------------------------------------------------------------------------
   StartServer (fixed)
----------------------------------------------------------------------------*/

func StartServer(cfg *state.Config) error {
    mu.Lock()
    defer mu.Unlock()

    // Already running?
    if serverCmd != nil {
        return fmt.Errorf("server already running")
    }

    dir := provision.ServerDir(*cfg)
    startScript := filepath.Join(dir, "start.sh")

    cmd := exec.Command("/bin/bash", startScript)
    cmd.Dir = dir

    stdout, _ := cmd.StdoutPipe()
    stderr, _ := cmd.StderrPipe()
    stdin, _ := cmd.StdinPipe()

    serverStdin = stdin
    serverCmd = cmd

    // Mark STARTING (not running)
    state.SetState(state.StateStarting)

    if err := cmd.Start(); err != nil {
        serverCmd = nil
        return fmt.Errorf("start server: %w", err)
    }

    /* -------------------------
       Log pumps
    --------------------------*/
    go pumpOutput(stdout, os.Stdout)
    go pumpOutput(stderr, os.Stderr)

    /* -------------------------
       Detect "Done" → running
    --------------------------*/
    go detectMinecraftReady(cfg)

    /* -------------------------
       Crash watcher
    --------------------------*/
    go func() {
        err := cmd.Wait()

        mu.Lock()
        defer mu.Unlock()

        if err != nil {
            state.RecordCrash(err)
            serverCmd = nil
            serverStdin = nil
            return
        }

        // Normal stop
        state.SetState(state.StateIdle)
        serverCmd = nil
        serverStdin = nil
    }()

    return nil
}

/* helper to pump logs */
func pumpOutput(r io.Reader, w *os.File) {
    buf := make([]byte, 4096)
    for {
        n, err := r.Read(buf)
        if n > 0 {
            w.Write(buf[:n])
        }
        if err != nil {
            return
        }
    }
}

/* Detects Minecraft "Done" and updates state */
func detectMinecraftReady(cfg *state.Config) {
    dir := provision.ServerDir(*cfg)
    logPath := filepath.Join(dir, "logs", "latest.log")

    deadline := time.Now().Add(5 * time.Minute) // FORGE NEEDS MORE TIME

    lastSize := int64(0)

    for time.Now().Before(deadline) {

        // Wait for log file to appear
        st, err := os.Stat(logPath)
        if err == nil {
            // ensure file is growing
            if st.Size() != lastSize {
                lastSize = st.Size()

                b, _ := os.ReadFile(logPath)
                s := string(b)

                // UNIVERSAL READY MATCHES
                if strings.Contains(s, "Done (") ||
                   strings.Contains(s, "For help, type \"help\"") ||
                   strings.Contains(s, "Successfully loaded forge") ||
                   strings.Contains(s, "Preparing spawn area: 100%") {

                    state.SetState(state.StateRunning)
                    state.SetError(nil)
                    return
                }
            }
        }

        time.Sleep(2 * time.Second)
    }

    state.SetState(state.StateError)
    state.SetError(fmt.Errorf("server failed to reach running state before timeout"))
}




/* --------------------------------------------------------------------------
   StopServer
----------------------------------------------------------------------------*/

func StopServer() error {
	mu.Lock()
	defer mu.Unlock()

	if serverCmd == nil {
		return fmt.Errorf("server not running")
	}

	state.SetState(state.StateStopping)

	// Try graceful stop
	if serverStdin != nil {
		_, _ = serverStdin.Write([]byte("save-all\n"))
		time.Sleep(2 * time.Second)
		_, _ = serverStdin.Write([]byte("stop\n"))
	}

	// Wait a moment
	time.Sleep(4 * time.Second)

	// If still running, force kill
	if serverCmd.Process != nil {
		_ = serverCmd.Process.Kill()
	}

	state.SetState(state.StateIdle)
	serverCmd = nil
	serverStdin = nil
	return nil
}

/* --------------------------------------------------------------------------
   RestartServer
----------------------------------------------------------------------------*/

func RestartServer(cfg *state.Config) error {
	if err := StopServer(); err != nil {
		// ignore if not running
	}

	return StartServer(cfg)
}

/* --------------------------------------------------------------------------
   SendConsoleCommand
----------------------------------------------------------------------------*/

func SendConsoleCommand(cmd string) error {
	mu.Lock()
	defer mu.Unlock()

	if serverStdin == nil {
		return fmt.Errorf("server console not available")
	}

	_, err := serverStdin.Write([]byte(cmd + "\n"))
	return err
}
