package agenthttp

import (
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"

	"zlh-agent/internal/provision"
	"zlh-agent/internal/provision/devcontainer"
	"zlh-agent/internal/provision/devcontainer/go"
	"zlh-agent/internal/provision/devcontainer/java"
	"zlh-agent/internal/provision/devcontainer/node"
	"zlh-agent/internal/provision/devcontainer/python"
	"zlh-agent/internal/provision/minecraft"
	"zlh-agent/internal/state"
	"zlh-agent/internal/system"
)

/* --------------------------------------------------------------------------
   Helpers
----------------------------------------------------------------------------*/
func fileExists(path string) bool {
	_, err := os.Stat(path)
	return err == nil
}

func dirExists(path string) bool {
	s, err := os.Stat(path)
	return err == nil && s.IsDir()
}

/* --------------------------------------------------------------------------
   Shared provision pipeline (installer + Minecraft verify)
----------------------------------------------------------------------------*/
func runProvisionPipeline(cfg *state.Config) error {
	state.SetState(state.StateInstalling)
	state.SetInstallStep("provision_all")

	if err := provision.ProvisionAll(*cfg); err != nil {
		state.SetError(err)
		state.SetState(state.StateError)
		return err
	}

	if strings.ToLower(cfg.Game) == "minecraft" {
		if err := minecraft.VerifyMinecraftInstallWithRepair(*cfg); err != nil {
			state.SetError(err)
			state.SetState(state.StateError)
			return fmt.Errorf("minecraft verification failed: %w", err)
		}
	}

	state.SetInstallStep("")
	state.SetState(state.StateIdle)
	return nil
}

/* --------------------------------------------------------------------------
   ensureProvisioned() — idempotent, unified
----------------------------------------------------------------------------*/
func ensureProvisioned(cfg *state.Config) error {

if cfg.ContainerType == "dev" {

    if !devcontainer.IsProvisioned() {
        if err := runProvisionPipeline(cfg); err != nil {
            return err
        }
    }

    var err error

    switch strings.ToLower(cfg.Runtime) {
    case "node":
        err = node.Verify(*cfg)
    case "python":
        err = python.Verify(*cfg)
    case "go":
        err = goenv.Verify(*cfg)
    case "java":
        err = java.Verify(*cfg)
    default:
        return fmt.Errorf("unsupported devcontainer runtime: %s", cfg.Runtime)
    }

    if err != nil {
        return err
    }

    // ✅ DEV READY = RUNNING
    state.SetState(state.StateRunning)
    state.SetError(nil)
    return nil
}


	dir := provision.ServerDir(*cfg)
	game := strings.ToLower(cfg.Game)
	variant := strings.ToLower(cfg.Variant)

	_ = os.MkdirAll(dir, 0o755)

	isSteam := provision.IsSteamGame(game)
	isForgeLike := variant == "forge" || variant == "neoforge"

	if isSteam {
		return runProvisionPipeline(cfg)
	}

	if isForgeLike {
		runSh := filepath.Join(dir, "run.sh")
		libraries := filepath.Join(dir, "libraries")

		if fileExists(runSh) && dirExists(libraries) {
			return nil
		}
		return runProvisionPipeline(cfg)
	}

	jar := filepath.Join(dir, "server.jar")
	if fileExists(jar) {
		return nil
	}

	return runProvisionPipeline(cfg)
}

/* --------------------------------------------------------------------------
   /config — the REAL provisioning trigger (async)
----------------------------------------------------------------------------*/
func handleConfig(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "POST only", http.StatusMethodNotAllowed)
		return
	}

	body, _ := io.ReadAll(r.Body)

	var cfg state.Config
	if err := json.Unmarshal(body, &cfg); err != nil {
		http.Error(w, "bad json", http.StatusBadRequest)
		return
	}

	if err := state.SaveConfig(&cfg); err != nil {
		http.Error(w, "save config failed: "+err.Error(), http.StatusInternalServerError)
		return
	}

	go func(c state.Config) {
		log.Println("[agent] async provision+start begin")

		if err := ensureProvisioned(&c); err != nil {
			log.Println("[agent] provision error:", err)
			return
		}

		if c.ContainerType != "dev" {

			if err := system.StartServer(&c); err != nil {
				log.Println("[agent] start error:", err)
				state.SetError(err)
				state.SetState(state.StateError)
				return
			}

			// -------------------------------------------------
			// FORGE / NEOFORGE: wait → stop → patch → restart
			// -------------------------------------------------
			game := strings.ToLower(c.Game)
			variant := strings.ToLower(c.Variant)

			if game == "minecraft" && (variant == "forge" || variant == "neoforge") {

				deadline := time.Now().Add(5 * time.Minute)
				for {
					if state.GetState() == state.StateRunning {
						break
					}
					if time.Now().After(deadline) {
						err := fmt.Errorf("forge did not reach running state")
						log.Println("[agent]", err)
						state.SetError(err)
						state.SetState(state.StateError)
						return
					}
					time.Sleep(2 * time.Second)
				}

				_ = system.StopServer()

				if err := minecraft.EnforceForgeServerProperties(c); err != nil {
					log.Println("[agent] forge post-start error:", err)
					state.SetError(err)
					state.SetState(state.StateError)
					return
				}

				if err := system.StartServer(&c); err != nil {
					log.Println("[agent] restart error:", err)
					state.SetError(err)
					state.SetState(state.StateError)
					return
				}
			}
		}

		log.Println("[agent] async provision+start complete")
	}(cfg)

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusAccepted)
	_, _ = w.Write([]byte(`{"ok": true, "state": "installing"}`))
}

/* --------------------------------------------------------------------------
   /start
----------------------------------------------------------------------------*/
func handleStart(w http.ResponseWriter, r *http.Request) {
	cfg, err := state.LoadConfig()
	if err != nil {
		http.Error(w, "no config: "+err.Error(), http.StatusBadRequest)
		return
	}

	if cfg.ContainerType == "dev" {
		http.Error(w, "dev containers do not support manual start", http.StatusBadRequest)
		return
	}

	if err := system.StartServer(cfg); err != nil {
		http.Error(w, "start error: "+err.Error(), http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	_, _ = w.Write([]byte(`{"ok": true, "state": "starting"}`))
}

/* --------------------------------------------------------------------------
   /stop
----------------------------------------------------------------------------*/
func handleStop(w http.ResponseWriter, r *http.Request) {
	if err := system.StopServer(); err != nil {
		http.Error(w, "stop error: "+err.Error(), http.StatusInternalServerError)
		return
	}
	w.WriteHeader(http.StatusNoContent)
}

/* --------------------------------------------------------------------------
   /restart
----------------------------------------------------------------------------*/
func handleRestart(w http.ResponseWriter, r *http.Request) {
	cfg, err := state.LoadConfig()
	if err != nil {
		http.Error(w, "no config", http.StatusBadRequest)
		return
	}

	if cfg.ContainerType == "dev" {
		http.Error(w, "dev containers do not support restart", http.StatusBadRequest)
		return
	}

	_ = system.StopServer()

	if err := system.StartServer(cfg); err != nil {
		http.Error(w, "restart error: "+err.Error(), http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	_, _ = w.Write([]byte(`{"ok": true, "state": "starting"}`))
}

/* --------------------------------------------------------------------------
   /status
----------------------------------------------------------------------------*/
func handleStatus(w http.ResponseWriter, r *http.Request) {
	cfg, _ := state.LoadConfig()

	resp := map[string]any{
		"state":       state.GetState(),
		"installStep": state.GetInstallStep(),
		"crashCount":  state.GetCrashCount(),
		"error":       nil,
		"config":      cfg,
		"timestamp":   time.Now().Unix(),
	}

	if err := state.GetError(); err != nil {
		resp["error"] = err.Error()
	}

	w.Header().Set("Content-Type", "application/json")
	_ = json.NewEncoder(w).Encode(resp)
}

/* --------------------------------------------------------------------------
   /console/command
----------------------------------------------------------------------------*/
func handleSendCommand(w http.ResponseWriter, r *http.Request) {
	cmd := r.URL.Query().Get("cmd")
	if cmd == "" {
		http.Error(w, "cmd required", http.StatusBadRequest)
		return
	}

	if err := system.SendConsoleCommand(cmd); err != nil {
		http.Error(w, "command error: "+err.Error(), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusNoContent)
}

/* --------------------------------------------------------------------------
   Router
----------------------------------------------------------------------------*/
func NewMux() *http.ServeMux {
	m := http.NewServeMux()

	m.HandleFunc("/config", handleConfig)
	m.HandleFunc("/start", handleStart)
	m.HandleFunc("/stop", handleStop)
	m.HandleFunc("/restart", handleRestart)
	m.HandleFunc("/status", handleStatus)
	m.HandleFunc("/console/command", handleSendCommand)

	registerWebSocket(m)

	m.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("ok"))
	})

	log.Println("[agent] routes registered")
	return m
}
