package transcode_test

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

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

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

const (
	vkTestSecret = "wv527nm6mvf3rgomfhd6"
	// vkGoldenSign is the base64url HMAC-SHA256 of the unsigned params below under
	// vkTestSecret, computed independently (a Python reference) — so a green test
	// exercises VK's real algorithm end to end, not a self-consistent fake.
	vkGoldenSign = "x7RUe9sKN05Bigm-pBIl8gLmfMwZCloPrwZaZEQAdOA"
)

// vkParams is the canonical VK launch-parameter set (without the sign) that
// vkGoldenSign covers.
func vkParams() url.Values {
	return url.Values{
		"vk_access_token_settings":     {""},
		"vk_app_id":                    {"6736218"},
		"vk_are_notifications_enabled": {"0"},
		"vk_is_app_user":               {"1"},
		"vk_is_favorite":               {"0"},
		"vk_language":                  {"ru"},
		"vk_platform":                  {"android"},
		"vk_ref":                       {"other"},
		"vk_ts":                        {"1546961916"},
		"vk_user_id":                   {"494075"},
	}
}

func vkLoginPayload(params, browserTz, displayName string) []byte {
	b := flatbuffers.NewBuilder(0)
	p := b.CreateString(params)
	tz := b.CreateString(browserTz)
	dn := b.CreateString(displayName)
	fb.VKLoginRequestStart(b)
	fb.VKLoginRequestAddParams(b, p)
	fb.VKLoginRequestAddBrowserTz(b, tz)
	fb.VKLoginRequestAddDisplayName(b, dn)
	b.Finish(fb.VKLoginRequestEnd(b))
	return b.FinishedBytes()
}

func TestVKAuthForwardsSeedFields(t *testing.T) {
	var gotBody map[string]string
	backend, cleanup := fakeBackend(t, func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path != "/api/v1/internal/sessions/vk" {
			t.Errorf("unexpected path %q", r.URL.Path)
		}
		_ = json.NewDecoder(r.Body).Decode(&gotBody)
		_, _ = w.Write([]byte(`{"token":"tok-vk","user_id":"u-vk","is_guest":false,"display_name":"Иван"}`))
	})
	defer cleanup()

	reg := transcode.NewRegistry(backend, nil, transcode.WithVKAuth(vkTestSecret))
	op, ok := reg.Lookup(transcode.MsgAuthVK)
	if !ok {
		t.Fatal("auth.vk not registered")
	}

	signed := vkParams()
	signed.Set("sign", vkGoldenSign)
	payload, err := op.Handler(context.Background(), transcode.Request{Payload: vkLoginPayload(signed.Encode(), "+03:00", "Иван Петров")})
	if err != nil {
		t.Fatalf("handler: %v", err)
	}
	sess := fb.GetRootAsSession(payload, 0)
	if string(sess.Token()) != "tok-vk" || string(sess.UserId()) != "u-vk" {
		t.Fatalf("session decoded wrong: token=%q user=%q", sess.Token(), sess.UserId())
	}
	// The verified vk_user_id and vk_language plus the client-supplied display name are
	// forwarded so the backend can seed a brand-new account.
	if gotBody["external_id"] != "494075" || gotBody["language_code"] != "ru" || gotBody["display_name"] != "Иван Петров" {
		t.Errorf("forwarded body = %+v, want external_id=494075 language_code=ru display_name=Иван Петров", gotBody)
	}
}

// TestVKAuthInvalidSign confirms a bad signature is a domain failure (invalid_vk_params)
// and the backend is never called.
func TestVKAuthInvalidSign(t *testing.T) {
	backend, cleanup := fakeBackend(t, func(http.ResponseWriter, *http.Request) {
		t.Error("backend must not be called when the sign is invalid")
	})
	defer cleanup()

	reg := transcode.NewRegistry(backend, nil, transcode.WithVKAuth(vkTestSecret))
	op, _ := reg.Lookup(transcode.MsgAuthVK)

	tampered := vkParams()
	tampered.Set("sign", "deadbeef")
	_, err := op.Handler(context.Background(), transcode.Request{Payload: vkLoginPayload(tampered.Encode(), "", "")})
	if code, ok := transcode.DomainCode(err); !ok || code != "invalid_vk_params" {
		t.Errorf("DomainCode = (%q, %v), want (invalid_vk_params, true)", code, ok)
	}
}

// TestVKAuthDisabledWithoutSecret confirms a blank VK app secret leaves auth.vk
// unregistered.
func TestVKAuthDisabledWithoutSecret(t *testing.T) {
	backend, cleanup := fakeBackend(t, func(http.ResponseWriter, *http.Request) {})
	defer cleanup()
	reg := transcode.NewRegistry(backend, nil)
	if _, ok := reg.Lookup(transcode.MsgAuthVK); ok {
		t.Error("auth.vk should be unregistered without a VK app secret")
	}
}
