package transcode_test

import (
	"context"
	"encoding/json"
	"net/http"
	"testing"

	flatbuffers "github.com/google/flatbuffers/go"

	"scrabble/gateway/internal/transcode"
	"scrabble/gateway/internal/vkid"
	fb "scrabble/pkg/fbs/scrabblefb"
)

func linkVKPayload(code, deviceID, verifier string) []byte {
	b := flatbuffers.NewBuilder(64)
	c := b.CreateString(code)
	d := b.CreateString(deviceID)
	v := b.CreateString(verifier)
	fb.LinkVKRequestStart(b)
	fb.LinkVKRequestAddCode(b, c)
	fb.LinkVKRequestAddDeviceId(b, d)
	fb.LinkVKRequestAddCodeVerifier(b, v)
	b.Finish(fb.LinkVKRequestEnd(b))
	return b.FinishedBytes()
}

// fakeVKIDExchanger records the exchange inputs and returns a canned identity.
type fakeVKIDExchanger struct {
	id                              vkid.Identity
	err                             error
	gotCode, gotDevice, gotVerifier string
}

func (f *fakeVKIDExchanger) Exchange(_ context.Context, code, deviceID, codeVerifier string) (vkid.Identity, error) {
	f.gotCode, f.gotDevice, f.gotVerifier = code, deviceID, codeVerifier
	return f.id, f.err
}

func TestLinkVKExchangesAndForwards(t *testing.T) {
	var gotExternalID string
	backend, cleanup := fakeBackend(t, func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path != "/api/v1/user/link/vk" {
			t.Errorf("path = %q", r.URL.Path)
		}
		var body struct {
			ExternalID string `json:"external_id"`
		}
		_ = json.NewDecoder(r.Body).Decode(&body)
		gotExternalID = body.ExternalID
		_, _ = w.Write([]byte(`{"status":"linked"}`))
	})
	defer cleanup()

	ex := &fakeVKIDExchanger{id: vkid.Identity{ExternalID: "777"}}
	reg := transcode.NewRegistry(backend, nil, transcode.WithVKLink(ex))
	op, ok := reg.Lookup(transcode.MsgLinkVK)
	if !ok {
		t.Fatal("link.vk.confirm not registered")
	}
	payload, err := op.Handler(context.Background(), transcode.Request{UserID: "u-1", Payload: linkVKPayload("the-code", "dev-1", "verifier-1")})
	if err != nil {
		t.Fatalf("handler: %v", err)
	}
	// The PKCE inputs from the wire must reach the exchanger verbatim...
	if ex.gotCode != "the-code" || ex.gotDevice != "dev-1" || ex.gotVerifier != "verifier-1" {
		t.Errorf("exchange got %q/%q/%q", ex.gotCode, ex.gotDevice, ex.gotVerifier)
	}
	// ...and the resolved vk id must be the one forwarded to the backend.
	if gotExternalID != "777" {
		t.Errorf("backend external_id = %q, want 777", gotExternalID)
	}
	if string(fb.GetRootAsLinkResult(payload, 0).Status()) != "linked" {
		t.Error("expected a linked result")
	}
}

func TestLinkVKUnregisteredWithoutExchanger(t *testing.T) {
	backend, cleanup := fakeBackend(t, func(http.ResponseWriter, *http.Request) {})
	defer cleanup()

	reg := transcode.NewRegistry(backend, nil)
	if _, ok := reg.Lookup(transcode.MsgLinkVK); ok {
		t.Error("link.vk.confirm must be unregistered when no VK ID exchanger is configured")
	}
	if _, ok := reg.Lookup(transcode.MsgLinkVKMerge); ok {
		t.Error("link.vk.merge must be unregistered when no VK ID exchanger is configured")
	}
}
