package main

// ARK auto-save restore endpoint. Sister to the existing files.go ops,
// but split into its own file because the semantics are ARK-specific:
// the agent handler walks the SavedArks/<subdir>/ layout, refuses if the
// container is running, and preserves the previously-active save by
// renaming it aside (NEVER deleting) so even abandoned attempts are
// recoverable.

import (
	"encoding/json"
	"io"
	"net/http"
	"strings"
	"time"

	"google.golang.org/protobuf/types/known/timestamppb"

	panelv1 "github.com/dbledeez/panel/proto/panel/v1"
)

// arkSaveRestoreAwait covers stop-aware file ops on a multi-GB save dir
// over Docker's CopyTar pipe. 2 minutes is well past the worst case for
// a few-hundred-MB rename + cp on local SSD.
const arkSaveRestoreAwait = 2 * 60 * time.Second

func (h *httpServer) arkSaveRestore(w http.ResponseWriter, r *http.Request) {
	id := r.PathValue("id")
	agentID, err := h.lookupInstance(r, id)
	if err != nil {
		writeError(w, http.StatusNotFound, "not_found", err.Error())
		return
	}
	conn := h.registry.get(agentID)
	if conn == nil {
		writeError(w, http.StatusNotFound, "agent_offline", "agent not connected")
		return
	}
	var req struct {
		SavedArksSubdir string `json:"saved_arks_subdir"`
		TargetFilename  string `json:"target_filename"`
	}
	body, err := io.ReadAll(io.LimitReader(r.Body, 1<<20))
	if err != nil {
		writeError(w, http.StatusBadRequest, "read", err.Error())
		return
	}
	if err := json.Unmarshal(body, &req); err != nil {
		writeError(w, http.StatusBadRequest, "bad_json", err.Error())
		return
	}
	subdir := strings.TrimSpace(req.SavedArksSubdir)
	target := strings.TrimSpace(req.TargetFilename)
	if subdir == "" || target == "" {
		writeError(w, http.StatusBadRequest, "missing_fields", "saved_arks_subdir and target_filename are required")
		return
	}

	corrID := newCorrelationID("arkrestore")
	if err := conn.Send(&panelv1.ControllerEnvelope{
		CorrelationId: corrID,
		SentAt:        timestamppb.Now(),
		Payload: &panelv1.ControllerEnvelope_ArkSaveRestore{
			ArkSaveRestore: &panelv1.ArkSaveRestoreRequest{
				InstanceId:      id,
				SavedArksSubdir: subdir,
				TargetFilename:  target,
			},
		},
	}); err != nil {
		writeError(w, http.StatusBadGateway, "forward", err.Error())
		return
	}
	env, err := h.pending.Await(r.Context(), corrID, arkSaveRestoreAwait)
	if err != nil {
		writeError(w, http.StatusGatewayTimeout, "timeout", err.Error())
		return
	}
	res := env.GetArkSaveRestoreResult()
	if res == nil {
		writeError(w, http.StatusInternalServerError, "bad_reply", "agent reply missing ark_save_restore_result")
		return
	}
	if res.Error != "" {
		// Agent's "refusing while running" message is operator-visible,
		// surface as 409 so the UI can show "stop the server first" inline.
		status := http.StatusBadRequest
		if strings.Contains(strings.ToLower(res.Error), "refusing to restore while server is running") {
			status = http.StatusConflict
		}
		writeError(w, status, "ark_restore", res.Error)
		return
	}
	writeJSON(w, http.StatusOK, map[string]any{
		"status":             "ok",
		"aside_filename":     res.AsideFilename,
		"installed_filename": res.InstalledFilename,
	})
}
