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"
)

func linkEmailConfirmPayload(email, code string) []byte {
	b := flatbuffers.NewBuilder(64)
	e := b.CreateString(email)
	c := b.CreateString(code)
	fb.LinkEmailConfirmStart(b)
	fb.LinkEmailConfirmAddEmail(b, e)
	fb.LinkEmailConfirmAddCode(b, c)
	b.Finish(fb.LinkEmailConfirmEnd(b))
	return b.FinishedBytes()
}

func linkTelegramPayload(data string) []byte {
	b := flatbuffers.NewBuilder(64)
	d := b.CreateString(data)
	fb.LinkTelegramRequestStart(b)
	fb.LinkTelegramRequestAddData(b, d)
	b.Finish(fb.LinkTelegramRequestEnd(b))
	return b.FinishedBytes()
}

func TestLinkEmailConfirmMergeRequired(t *testing.T) {
	backend, cleanup := fakeBackend(t, func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path != "/api/v1/user/link/email/confirm" {
			t.Errorf("path = %q", r.URL.Path)
		}
		_, _ = w.Write([]byte(`{"status":"merge_required","secondary_user_id":"b-1","secondary_display_name":"Ann","secondary_games":7,"secondary_friends":3}`))
	})
	defer cleanup()

	reg := transcode.NewRegistry(backend, nil)
	op, ok := reg.Lookup(transcode.MsgLinkEmailConfirm)
	if !ok {
		t.Fatal("link.email.confirm not registered")
	}
	payload, err := op.Handler(context.Background(), transcode.Request{UserID: "u-1", Payload: linkEmailConfirmPayload("e@x.com", "123456")})
	if err != nil {
		t.Fatalf("handler: %v", err)
	}
	lr := fb.GetRootAsLinkResult(payload, 0)
	if string(lr.Status()) != "merge_required" || string(lr.SecondaryUserId()) != "b-1" ||
		string(lr.SecondaryDisplayName()) != "Ann" || lr.SecondaryGames() != 7 || lr.SecondaryFriends() != 3 {
		t.Fatalf("link result = %q/%q/%q/%d/%d", lr.Status(), lr.SecondaryUserId(), lr.SecondaryDisplayName(), lr.SecondaryGames(), lr.SecondaryFriends())
	}
	if lr.Session(nil) != nil {
		t.Error("a merge_required result must not carry a session")
	}
}

func TestLinkEmailMergeSwitchesSession(t *testing.T) {
	backend, cleanup := fakeBackend(t, func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path != "/api/v1/user/link/email/merge" {
			t.Errorf("path = %q", r.URL.Path)
		}
		_, _ = w.Write([]byte(`{"status":"merged","token":"tok-9","profile":{"user_id":"a-1","display_name":"Kaya","is_guest":false}}`))
	})
	defer cleanup()

	reg := transcode.NewRegistry(backend, nil)
	op, _ := reg.Lookup(transcode.MsgLinkEmailMerge)
	payload, err := op.Handler(context.Background(), transcode.Request{UserID: "u-1", Payload: linkEmailConfirmPayload("e@x.com", "123456")})
	if err != nil {
		t.Fatalf("handler: %v", err)
	}
	lr := fb.GetRootAsLinkResult(payload, 0)
	if string(lr.Status()) != "merged" {
		t.Fatalf("status = %q, want merged", lr.Status())
	}
	sess := lr.Session(nil)
	if sess == nil {
		t.Fatal("a switched merge must carry a session")
	}
	if string(sess.Token()) != "tok-9" || string(sess.UserId()) != "a-1" {
		t.Fatalf("session = %q/%q, want tok-9/a-1", sess.Token(), sess.UserId())
	}
}

func TestLinkTelegramValidatesAndForwards(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/telegram" {
			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()

	reg := transcode.NewRegistry(backend, fakeValidator{user: connector.User{ExternalID: "42"}})
	op, ok := reg.Lookup(transcode.MsgLinkTelegram)
	if !ok {
		t.Fatal("link.telegram.confirm not registered")
	}
	payload, err := op.Handler(context.Background(), transcode.Request{UserID: "u-1", Payload: linkTelegramPayload("id=42&hash=x")})
	if err != nil {
		t.Fatalf("handler: %v", err)
	}
	if gotExternalID != "42" {
		t.Errorf("backend external_id = %q, want 42 (the gateway-validated id)", gotExternalID)
	}
	if string(fb.GetRootAsLinkResult(payload, 0).Status()) != "linked" {
		t.Error("expected a linked result")
	}
}
