package notify

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

	"scrabble/backend/internal/engine"
	"scrabble/pkg/wire"
)

// The builders below encode the nested wire tables embedded in enriched event
// payloads. They map the domain's already-resolved values (notify.* payload structs
// and the decoded engine.MoveRecord) to the neutral scrabble/pkg/wire structs and
// delegate the FlatBuffers construction to package wire — the single definition of the
// nested-table layout shared with the gateway transcoder. Each returns the offset of
// the table it built; callers must build every nested table before opening the parent.

// toWireGame maps a GameSummary to the shared wire.GameView.
func toWireGame(g GameSummary) wire.GameView {
	seats := make([]wire.SeatView, len(g.Seats))
	for i, s := range g.Seats {
		seats[i] = wire.SeatView{
			Seat:        s.Seat,
			AccountID:   s.AccountID,
			Score:       s.Score,
			HintsUsed:   s.HintsUsed,
			IsWinner:    s.IsWinner,
			DisplayName: s.DisplayName,
		}
	}
	return wire.GameView{
		ID:                   g.ID,
		Variant:              g.Variant,
		DictVersion:          g.DictVersion,
		Status:               g.Status,
		Players:              g.Players,
		ToMove:               g.ToMove,
		TurnTimeoutSecs:      g.TurnTimeoutSecs,
		MultipleWordsPerTurn: g.MultipleWordsPerTurn,
		VsAI:                 g.VsAI,
		Kind:                 g.Kind,
		MoveCount:            g.MoveCount,
		EndReason:            g.EndReason,
		Seats:                seats,
		LastActivityUnix:     g.LastActivityUnix,
		NoRepeatWords:        g.NoRepeatWords,
	}
}

// buildGameView builds a GameView table from a GameSummary and returns its offset.
func buildGameView(b *flatbuffers.Builder, g GameSummary) flatbuffers.UOffsetT {
	return wire.BuildGameView(b, toWireGame(g))
}

// buildMoveRecord builds a MoveRecord table from a decoded engine move and returns its
// offset (Count is the engine count: the number of tiles swapped on an exchange, zero
// otherwise).
func buildMoveRecord(b *flatbuffers.Builder, m engine.MoveRecord) flatbuffers.UOffsetT {
	tiles := make([]wire.TileRecord, len(m.Tiles))
	for i, t := range m.Tiles {
		tiles[i] = wire.TileRecord{Row: t.Row, Col: t.Col, Letter: t.Letter, Blank: t.Blank}
	}
	return wire.BuildMoveRecord(b, wire.MoveRecord{
		Player:  m.Player,
		Action:  m.Action.String(),
		Dir:     m.Dir.String(),
		MainRow: m.MainRow,
		MainCol: m.MainCol,
		Tiles:   tiles,
		Words:   m.Words,
		Count:   m.Count,
		Score:   m.Score,
		Total:   m.Total,
	})
}

// buildStateView builds a StateView table from a PlayerState and returns its offset.
func buildStateView(b *flatbuffers.Builder, s PlayerState) flatbuffers.UOffsetT {
	alphabet := make([]wire.AlphabetEntry, len(s.Alphabet))
	for i, e := range s.Alphabet {
		alphabet[i] = wire.AlphabetEntry{Index: e.Index, Letter: e.Letter, Value: e.Value}
	}
	return wire.BuildStateView(b, wire.StateView{
		Game:           toWireGame(s.Game),
		Seat:           s.Seat,
		Rack:           s.Rack,
		BagLen:         s.BagLen,
		HintsRemaining: s.HintsRemaining,
		Alphabet:       alphabet,
	})
}

// buildAccountRef builds an AccountRef table and returns its offset.
func buildAccountRef(b *flatbuffers.Builder, a AccountRef) flatbuffers.UOffsetT {
	return wire.BuildAccountRef(b, wire.AccountRef{AccountID: a.AccountID, DisplayName: a.DisplayName})
}

// buildInvitation builds an Invitation table from an InvitationSummary and returns its offset.
func buildInvitation(b *flatbuffers.Builder, inv InvitationSummary) flatbuffers.UOffsetT {
	invitees := make([]wire.InvitationInvitee, len(inv.Invitees))
	for i, iv := range inv.Invitees {
		invitees[i] = wire.InvitationInvitee{
			AccountID:   iv.AccountID,
			DisplayName: iv.DisplayName,
			Seat:        iv.Seat,
			Response:    iv.Response,
		}
	}
	return wire.BuildInvitation(b, wire.Invitation{
		ID:                   inv.ID,
		Inviter:              wire.AccountRef{AccountID: inv.Inviter.AccountID, DisplayName: inv.Inviter.DisplayName},
		Invitees:             invitees,
		Variant:              inv.Variant,
		TurnTimeoutSecs:      inv.TurnTimeoutSecs,
		HintsAllowed:         inv.HintsAllowed,
		HintsPerPlayer:       inv.HintsPerPlayer,
		MultipleWordsPerTurn: inv.MultipleWordsPerTurn,
		DropoutTiles:         inv.DropoutTiles,
		Status:               inv.Status,
		GameID:               inv.GameID,
		ExpiresAtUnix:        inv.ExpiresAtUnix,
	})
}
