// hooks_test.go — pre/post backup hook semantics (P2R-11).
package runner

import (
	"context"
	"strings"
	"testing"

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

// TestPreHookFailureAbortsBackup: pre_hook exits 1 → restic never
// runs, job is recorded failed with the hook's error.
func TestPreHookFailureAbortsBackup(t *testing.T) {
	t.Parallel()
	// Restic script that records every invocation. If restic was
	// called we'll see "restic-was-here" in the captured log.
	bin := setupScript(t, `echo "restic-was-here"`)
	tx := &fakeSender{}
	r := New(Config{ResticBin: bin}, tx, 0)

	err := r.RunBackup(context.Background(), "job-pre",
		[]string{"/etc"}, nil, []string{"tag"},
		BackupHooks{Pre: "exit 1"})
	if err == nil {
		t.Fatal("expected RunBackup to return an error from failed pre_hook")
	}
	if !strings.Contains(err.Error(), "pre_hook failed") {
		t.Fatalf("error message: %q (want 'pre_hook failed')", err)
	}
	// job.finished arrived with status=failed.
	finEnv := firstEnvOfType(t, tx.envs, api.MsgJobFinished)
	var fin api.JobFinishedPayload
	_ = finEnv.UnmarshalPayload(&fin)
	if fin.Status != api.JobFailed {
		t.Fatalf("status: %q, want failed", fin.Status)
	}
	// restic must NOT have run.
	for _, env := range tx.envs {
		if env.Type != api.MsgLogStream {
			continue
		}
		var l api.LogStreamLine
		_ = env.UnmarshalPayload(&l)
		if strings.Contains(l.Payload, "restic-was-here") {
			t.Fatal("restic was invoked despite pre_hook failure")
		}
	}
}

// TestPostHookRunsAfterBackup: post_hook fires after a successful
// backup and receives RM_JOB_STATUS=succeeded in the env.
func TestPostHookRunsAfterBackup(t *testing.T) {
	t.Parallel()
	bin := setupScript(t, `
case "$1" in
  backup)    echo '{"message_type":"summary","snapshot_id":"abc"}' ;;
  snapshots) echo '[]' ;;
  stats)     echo '{"total_size":0,"total_uncompressed_size":0,"snapshots_count":0,"total_file_count":0,"total_blob_count":0}' ;;
  *)         exit 0 ;;
esac
`)
	tx := &fakeSender{}
	r := New(Config{ResticBin: bin}, tx, 0)

	post := `echo "post-status=$RM_JOB_STATUS phase=$RM_HOOK_PHASE"`
	if err := r.RunBackup(context.Background(), "job-post",
		[]string{"/etc"}, nil, nil, BackupHooks{Post: post}); err != nil {
		t.Fatalf("RunBackup: %v", err)
	}

	// Walk log.stream envelopes; one of them should be the post-hook
	// line with the expected status.
	var found bool
	for _, env := range tx.envs {
		if env.Type != api.MsgLogStream {
			continue
		}
		var l api.LogStreamLine
		_ = env.UnmarshalPayload(&l)
		if strings.Contains(l.Payload, "post-status=succeeded") &&
			strings.Contains(l.Payload, "phase=post") {
			found = true
			break
		}
	}
	if !found {
		t.Fatal("post_hook output not found in log.stream envelopes")
	}
}
