// diff_test.go — covers POST /api/hosts/{id}/snapshots/diff (P3-09).
package http

import (
	"context"
	"encoding/json"
	stdhttp "net/http"
	"net/url"
	"strings"
	"testing"
	"time"

	"github.com/coder/websocket"

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

// TestSnapshotDiffHappyPath verifies a valid two-snapshot form ships
// a JobDiff command.run with the right payload.
func TestSnapshotDiffHappyPath(t *testing.T) {
	t.Parallel()
	srv, ts, st := rawTestServerWithUI(t)
	hostID, token := enrolHostForUI(t, srv, st, "diff-host")
	a, b := seedTwoSnapshots(t, st, hostID, "diff-host")
	c := agentDial(t, srv, ts, hostID, token)
	sendHello(t, c, "diff-host")
	_ = drainUntil(t, c, api.MsgScheduleSet)
	cookie := loginAsAdmin(t, st)

	form := url.Values{
		"snapshot_a": {a},
		"snapshot_b": {b},
	}
	req, _ := stdhttp.NewRequest("POST",
		ts.URL+"/hosts/"+hostID+"/snapshots/diff",
		strings.NewReader(form.Encode()))
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Set("HX-Request", "true")
	req.AddCookie(cookie)
	client := &stdhttp.Client{
		CheckRedirect: func(*stdhttp.Request, []*stdhttp.Request) error {
			return stdhttp.ErrUseLastResponse
		},
	}
	res, err := client.Do(req)
	if err != nil {
		t.Fatalf("do: %v", err)
	}
	defer res.Body.Close()
	if res.StatusCode != stdhttp.StatusNoContent {
		t.Fatalf("status: got %d, want 204", res.StatusCode)
	}
	if res.Header.Get("HX-Redirect") == "" {
		t.Fatal("expected HX-Redirect to live job page")
	}

	deadline := time.Now().Add(2 * time.Second)
	var got api.Envelope
	for time.Now().Before(deadline) {
		ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
		mt, raw, rerr := c.Read(ctx)
		cancel()
		if rerr != nil {
			break
		}
		if mt != websocket.MessageText {
			continue
		}
		if !strings.Contains(string(raw), `"kind":"diff"`) {
			continue
		}
		_ = json.Unmarshal(raw, &got)
		break
	}
	if got.Type != api.MsgCommandRun {
		t.Fatal("never received diff command.run")
	}
	var cp api.CommandRunPayload
	_ = got.UnmarshalPayload(&cp)
	if cp.Diff == nil {
		t.Fatal("diff payload nil")
	}
	if cp.Diff.SnapshotA != a || cp.Diff.SnapshotB != b {
		t.Fatalf("diff payload: got %+v want a=%s b=%s", cp.Diff, a, b)
	}
}

// TestSnapshotDiffSameID rejects diff(a,a) with 422.
func TestSnapshotDiffSameID(t *testing.T) {
	t.Parallel()
	srv, ts, st := rawTestServerWithUI(t)
	hostID, _ := enrolHostForUI(t, srv, st, "diff-same")
	a := seedSnapshot(t, st, hostID, "diff-same")
	cookie := loginAsAdmin(t, st)

	form := url.Values{"snapshot_a": {a}, "snapshot_b": {a}}
	req, _ := stdhttp.NewRequest("POST",
		ts.URL+"/hosts/"+hostID+"/snapshots/diff",
		strings.NewReader(form.Encode()))
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	req.AddCookie(cookie)
	res, err := stdhttp.DefaultClient.Do(req)
	if err != nil {
		t.Fatalf("do: %v", err)
	}
	defer res.Body.Close()
	if res.StatusCode != stdhttp.StatusUnprocessableEntity {
		t.Fatalf("status: got %d, want 422", res.StatusCode)
	}
	_ = srv
}

// TestSnapshotDiffUnknownID rejects ids not in the host's snapshot list.
func TestSnapshotDiffUnknownID(t *testing.T) {
	t.Parallel()
	srv, ts, st := rawTestServerWithUI(t)
	hostID, _ := enrolHostForUI(t, srv, st, "diff-unknown")
	_ = seedSnapshot(t, st, hostID, "diff-unknown")
	cookie := loginAsAdmin(t, st)

	form := url.Values{"snapshot_a": {"deadbeef"}, "snapshot_b": {"cafebabe"}}
	req, _ := stdhttp.NewRequest("POST",
		ts.URL+"/hosts/"+hostID+"/snapshots/diff",
		strings.NewReader(form.Encode()))
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	req.AddCookie(cookie)
	res, err := stdhttp.DefaultClient.Do(req)
	if err != nil {
		t.Fatalf("do: %v", err)
	}
	defer res.Body.Close()
	if res.StatusCode != stdhttp.StatusUnprocessableEntity {
		t.Fatalf("status: got %d, want 422", res.StatusCode)
	}
	_ = srv
}
