package hub

import (
	"context"
	"net/http"
	"testing"
)

// FetchDesiredState GETs the SELF-SCOPED path with the bearer token and decodes the response.
func TestFetchDesiredState_PathAuthAndDecode(t *testing.T) {
	var gotPath, gotAuth, gotMethod string
	c := testClient(func(r *http.Request) (*http.Response, error) {
		gotPath = r.URL.Path
		gotAuth = r.Header.Get("Authorization")
		gotMethod = r.Method
		return httpResp(200, `{"generation":4,"desired_state":{"guests":[
			{"vmid":100,"run":"running"},
			{"vmid":200,"decommission":true}
		],"restore_directive":{"mode":"guest_loss","archive":"local:backup/x","vmid":100}}}`), nil
	})

	resp, err := c.FetchDesiredState(context.Background())
	if err != nil {
		t.Fatalf("FetchDesiredState: %v", err)
	}
	// Self-scoped: the client only ever fetches ITS OWN host (the configured host_id).
	if gotMethod != http.MethodGet || gotPath != "/api/v1/hosts/demo-host-01/desired-state" {
		t.Errorf("request = %s %s, want GET /api/v1/hosts/demo-host-01/desired-state", gotMethod, gotPath)
	}
	if gotAuth != "Bearer super-secret-bearer-key" {
		t.Errorf("auth header = %q, want the per-host bearer", gotAuth)
	}
	if resp.Generation != 4 {
		t.Errorf("generation = %d, want 4", resp.Generation)
	}
	if len(resp.DesiredState.Guests) != 2 {
		t.Fatalf("guests = %d, want 2", len(resp.DesiredState.Guests))
	}
	if resp.DesiredState.Guests[0].Run != "running" || !resp.DesiredState.Guests[1].Decommission {
		t.Errorf("guests = %+v", resp.DesiredState.Guests)
	}
	// Forward-compat restore_directive is carried through (consumed in 10D).
	if resp.DesiredState.RestoreDirective == nil || resp.DesiredState.RestoreDirective.Mode != "guest_loss" {
		t.Errorf("restore_directive = %+v, want carried (mode guest_loss)", resp.DesiredState.RestoreDirective)
	}
}

// A non-2xx response is a typed HTTPError (e.g. a 403 self-scope refusal from the hub).
func TestFetchDesiredState_HTTPError(t *testing.T) {
	c := testClient(func(r *http.Request) (*http.Response, error) {
		return httpResp(403, `Forbidden: host_id mismatch`), nil
	})
	_, err := c.FetchDesiredState(context.Background())
	if err == nil {
		t.Fatal("expected an error on 403")
	}
	var he *HTTPError
	if !asHTTPError(err, &he) || he.StatusCode != 403 {
		t.Errorf("err = %v, want HTTPError 403", err)
	}
}

func asHTTPError(err error, target **HTTPError) bool {
	for err != nil {
		if he, ok := err.(*HTTPError); ok {
			*target = he
			return true
		}
		type unwrapper interface{ Unwrap() error }
		if u, ok := err.(unwrapper); ok {
			err = u.Unwrap()
		} else {
			return false
		}
	}
	return false
}
