package transcode_test

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

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

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

// fakeValidator stands in for the connector's ValidateInitData RPC.
type fakeValidator struct {
	user connector.User
	err  error
}

func (f fakeValidator) ValidateInitData(context.Context, string) (connector.User, error) {
	return f.user, f.err
}

func (f fakeValidator) ValidateLoginWidget(context.Context, string) (connector.User, error) {
	return f.user, f.err
}

func telegramLoginPayload(initData string) []byte {
	b := flatbuffers.NewBuilder(0)
	off := b.CreateString(initData)
	fb.TelegramLoginRequestStart(b)
	fb.TelegramLoginRequestAddInitData(b, off)
	b.Finish(fb.TelegramLoginRequestEnd(b))
	return b.FinishedBytes()
}

func TestTelegramAuthForwardsSeedFields(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/telegram" {
			t.Errorf("unexpected path %q", r.URL.Path)
		}
		_ = json.NewDecoder(r.Body).Decode(&gotBody)
		_, _ = w.Write([]byte(`{"token":"tok-tg","user_id":"u-tg","is_guest":false,"display_name":"Иван"}`))
	})
	defer cleanup()

	v := fakeValidator{user: connector.User{ExternalID: "42", Username: "neo", FirstName: "Иван", LanguageCode: "ru"}}
	reg := transcode.NewRegistry(backend, v)
	op, ok := reg.Lookup(transcode.MsgAuthTelegram)
	if !ok {
		t.Fatal("auth.telegram not registered")
	}

	payload, err := op.Handler(context.Background(), transcode.Request{Payload: telegramLoginPayload("start_param=verudit_ru-scrabble_en&query_id=abc")})
	if err != nil {
		t.Fatalf("handler: %v", err)
	}
	sess := fb.GetRootAsSession(payload, 0)
	if string(sess.Token()) != "tok-tg" || string(sess.UserId()) != "u-tg" {
		t.Fatalf("session decoded wrong: token=%q user=%q", sess.Token(), sess.UserId())
	}
	// The validated launch fields are forwarded so the backend can seed a new account.
	if gotBody["external_id"] != "42" || gotBody["language_code"] != "ru" || gotBody["first_name"] != "Иван" {
		t.Errorf("forwarded body = %+v, want external_id=42 language_code=ru first_name=Иван", gotBody)
	}
	// start_param is parsed out of the validated initData and forwarded so the backend can
	// seed the new account's variant preferences from a promo deep link.
	if gotBody["start_param"] != "verudit_ru-scrabble_en" {
		t.Errorf("forwarded start_param = %q, want verudit_ru-scrabble_en", gotBody["start_param"])
	}
}

func TestTelegramAuthInvalidInitData(t *testing.T) {
	backend, cleanup := fakeBackend(t, func(http.ResponseWriter, *http.Request) {
		t.Error("backend must not be called when initData is invalid")
	})
	defer cleanup()

	reg := transcode.NewRegistry(backend, fakeValidator{err: connector.ErrInvalidInitData})
	op, _ := reg.Lookup(transcode.MsgAuthTelegram)

	_, err := op.Handler(context.Background(), transcode.Request{Payload: telegramLoginPayload("bad")})
	if code, ok := transcode.DomainCode(err); !ok || code != "invalid_init_data" {
		t.Errorf("DomainCode = (%q, %v), want (invalid_init_data, true)", code, ok)
	}
}

// TestTelegramAuthDisabledWithoutConnector confirms a nil validator leaves
// auth.telegram unregistered.
func TestTelegramAuthDisabledWithoutConnector(t *testing.T) {
	backend, cleanup := fakeBackend(t, func(http.ResponseWriter, *http.Request) {})
	defer cleanup()
	reg := transcode.NewRegistry(backend, nil)
	if _, ok := reg.Lookup(transcode.MsgAuthTelegram); ok {
		t.Error("auth.telegram should be unregistered without a connector")
	}
}
