package server

import (
	"encoding/json"
	"html/template"
	"strings"

	"scrabble/backend/internal/adminconsole"
	"scrabble/backend/internal/engine"
	"scrabble/backend/internal/game"
)

// The admin game-replay payload embedded in the game_detail page for its vanilla-JS stepper:
// the premium board layout, the seats and one step per replay frame (the dealt racks, then
// one per move) carrying the resulting racks, scores, turn cursor and bag size. Letters are
// upper-cased for display and carry their tile value (0 renders without a subscript).

type replayTileJSON struct {
	L string `json:"l"`
	V int    `json:"v"`
	B bool   `json:"b,omitempty"`
}

type replayPlaceJSON struct {
	R int    `json:"r"`
	C int    `json:"c"`
	L string `json:"l"`
	V int    `json:"v"`
	B bool   `json:"b,omitempty"`
}

type replayMoveJSON struct {
	Seat       int               `json:"seat"`
	Action     string            `json:"action"`
	Words      []string          `json:"words,omitempty"`
	Score      int               `json:"score"`
	Placements []replayPlaceJSON `json:"placements,omitempty"`
	Exchanged  []replayTileJSON  `json:"exchanged,omitempty"`
}

type replayStepJSON struct {
	Move   *replayMoveJSON    `json:"move"`
	Drawn  []replayTileJSON   `json:"drawn,omitempty"`
	Racks  [][]replayTileJSON `json:"racks"`
	Scores []int              `json:"scores"`
	ToMove int                `json:"toMove"`
	BagLen int                `json:"bagLen"`
}

type replaySeatJSON struct {
	Seat      int    `json:"seat"`
	Name      string `json:"name"`
	AccountID string `json:"accountId"`
}

type replayDataJSON struct {
	Centre  [2]int           `json:"centre"`
	Premium [][]string       `json:"premium"`
	Seats   []replaySeatJSON `json:"seats"`
	Steps   []replayStepJSON `json:"steps"`
}

// buildReplayJSON renders a game's replay timeline as the JSON the stepper consumes, resolving
// each tile's value from the variant's alphabet. The result is safe to embed in a <script>:
// encoding/json escapes <, > and & to \u00xx, so a user-chosen seat name cannot break out.
func buildReplayJSON(variant engine.Variant, tl game.ReplayTimelineView, seats []adminconsole.SeatRow) (template.JS, error) {
	values := map[string]int{}
	if table, err := engine.AlphabetTable(variant); err == nil {
		for _, e := range table {
			values[e.Letter] = e.Value
		}
	}
	tile := func(letter string, blank bool) replayTileJSON {
		v := 0
		if !blank {
			v = values[strings.ToLower(letter)]
		}
		return replayTileJSON{L: strings.ToUpper(letter), V: v, B: blank}
	}
	tiles := func(letters []string) []replayTileJSON {
		out := make([]replayTileJSON, len(letters))
		for i, l := range letters {
			out[i] = tile(l, l == "?")
		}
		return out
	}

	data := replayDataJSON{Centre: [2]int{7, 7}, Premium: premiumGrid(variant)}
	for _, s := range seats {
		data.Seats = append(data.Seats, replaySeatJSON{Seat: s.Seat, Name: s.DisplayName, AccountID: s.AccountID})
	}
	for _, st := range tl.Steps {
		js := replayStepJSON{
			Racks:  make([][]replayTileJSON, len(st.Racks)),
			Scores: st.Scores,
			ToMove: st.ToMove,
			BagLen: st.BagLen,
			Drawn:  tiles(st.Drawn),
		}
		for i, r := range st.Racks {
			js.Racks[i] = tiles(r)
		}
		if st.Move != nil {
			m := &replayMoveJSON{Seat: st.Move.Seat, Action: st.Move.Action, Words: st.Move.Words, Score: st.Move.Score}
			for _, p := range st.Move.Tiles {
				t := tile(p.Letter, p.Blank)
				m.Placements = append(m.Placements, replayPlaceJSON{R: p.Row, C: p.Col, L: t.L, V: t.V, B: t.B})
			}
			m.Exchanged = tiles(st.Move.Exchanged)
			js.Move = m
		}
		data.Steps = append(data.Steps, js)
	}
	b, err := json.Marshal(data)
	if err != nil {
		return "", err
	}
	return template.JS(b), nil
}

// premiumRows is the standard Scrabble premium-square layout: T triple-word, D double-word,
// t triple-letter, d double-letter, * the centre double-word, '.' a plain square.
var premiumRows = []string{
	"T..d...T...d..T",
	".D...t...t...D.",
	"..D...d.d...D..",
	"d..D...d...D..d",
	"....D.....D....",
	".t...t...t...t.",
	"..d...d.d...d..",
	"T..d...*...d..T",
	"..d...d.d...d..",
	".t...t...t...t.",
	"....D.....D....",
	"d..D...d...D..d",
	"..D...d.d...D..",
	".D...t...t...D.",
	"T..d...T...d..T",
}

// premiumGrid returns the 15×15 premium-square codes ("tw"/"dw"/"tl"/"dl"/"") for variant.
// Erudite carries no double-word premium on the centre square (only the start star).
func premiumGrid(variant engine.Variant) [][]string {
	code := func(ch byte) string {
		switch ch {
		case 'T':
			return "tw"
		case 'D', '*':
			return "dw"
		case 't':
			return "tl"
		case 'd':
			return "dl"
		}
		return ""
	}
	grid := make([][]string, len(premiumRows))
	for r, row := range premiumRows {
		grid[r] = make([]string, len(row))
		for c := 0; c < len(row); c++ {
			grid[r][c] = code(row[c])
		}
	}
	if variant == engine.VariantErudit {
		grid[7][7] = ""
	}
	return grid
}
