package main

import (
	"context"
	"fmt"
	"log/slog"
	"time"

	"gitea.dcglab.co.uk/steve/restic-manager/internal/agent/updater"
	"gitea.dcglab.co.uk/steve/restic-manager/internal/agent/wsclient"
	"gitea.dcglab.co.uk/steve/restic-manager/internal/api"
)

// runUpdate handles a server-dispatched command.update. It logs progress
// via log.stream so the live job page captures pre-restart state, then
// calls the platform updater. On Linux the updater calls os.Exit; on
// Windows it spawns a detached helper and returns, with the agent then
// exiting.
//
// The terminal job state is set by the server, not the agent: success
// is "agent re-hellos with matching version" rather than anything the
// agent itself can assert. The only `job.finished` we send from here is
// on the failure path, before any restart attempt.
func (d *dispatcher) runUpdate(ctx context.Context, p api.CommandUpdatePayload, tx wsclient.Sender) {
	logf := func(format string, args ...any) {
		line := fmt.Sprintf(format, args...)
		slog.Info("ws agent: update: " + line)
		env, err := api.Marshal(api.MsgLogStream, "", api.LogStreamLine{
			JobID:   p.JobID,
			TS:      time.Now().UTC(),
			Stream:  api.LogStdout,
			Payload: line,
		})
		if err == nil {
			_ = tx.Send(env)
		}
	}

	startedEnv, err := api.Marshal(api.MsgJobStarted, "", api.JobStartedPayload{
		JobID:     p.JobID,
		Kind:      api.JobUpdate,
		StartedAt: time.Now().UTC(),
	})
	if err == nil {
		_ = tx.Send(startedEnv)
	}

	logf("fetching new binary from %s", d.serverURL)
	if err := updater.Update(ctx, d.serverURL); err != nil {
		logf("update failed: %v", err)
		finishedEnv, mErr := api.Marshal(api.MsgJobFinished, "", api.JobFinishedPayload{
			JobID:      p.JobID,
			Status:     api.JobFailed,
			FinishedAt: time.Now().UTC(),
			Error:      err.Error(),
		})
		if mErr == nil {
			_ = tx.Send(finishedEnv)
		}
		return
	}
	// Unreachable on Linux (Update calls os.Exit). On Windows control
	// returns here while the detached helper does the swap-and-restart;
	// the agent then exits cleanly so SCM hands off.
}
