package router_test

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

	"galaxy/model/order"
	"galaxy/model/report"
	"galaxy/model/rest"

	"galaxy/game/internal/router"
	"galaxy/game/internal/router/handler"

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
)

var (
	commandNoErrorsStatus = http.StatusAccepted
	commandDefaultActor   = "Gorlum"
	apiCommandMethod      = "PUT"
	apiCommandPath        = "/api/v1/command"
	apiOrderPath          = "/api/v1/order"
	validId1              = id()
	validId2              = id()
	invalidId             = "fd091c69-5976-4775-b2f9-7ba77735afb"
)

func id() string {
	return uuid.New().String()
}

type dummyExecutor struct {
	CommandsExecuted int

	// ValidateOrderResult, when non-nil, is returned from ValidateOrder.
	// When nil, ValidateOrder synthesises an order from the received args
	// so the response body is non-empty for status assertions.
	ValidateOrderResult *order.UserGamesOrder
	ValidateOrderErr    error

	// FetchOrder controls and observes calls to FetchOrder.
	FetchOrderActor  string
	FetchOrderTurn   uint
	FetchOrderResult *order.UserGamesOrder
	FetchOrderOK     bool
	FetchOrderErr    error

	// FetchBattle controls and observes calls to FetchBattle.
	FetchBattleTurn   uint
	FetchBattleID     uuid.UUID
	FetchBattleResult *report.BattleReport
	FetchBattleOK     bool
	FetchBattleErr    error
}

func (e *dummyExecutor) ValidateOrder(actor string, cmd ...order.DecodableCommand) (*order.UserGamesOrder, error) {
	e.CommandsExecuted = len(cmd)
	if e.ValidateOrderErr != nil {
		return nil, e.ValidateOrderErr
	}
	if e.ValidateOrderResult != nil {
		return e.ValidateOrderResult, nil
	}
	return &order.UserGamesOrder{
		GameID:    uuid.New(),
		UpdatedAt: 1,
		Commands:  append([]order.DecodableCommand(nil), cmd...),
	}, nil
}

func (e *dummyExecutor) FetchOrder(actor string, turn uint) (*order.UserGamesOrder, bool, error) {
	e.FetchOrderActor = actor
	e.FetchOrderTurn = turn
	return e.FetchOrderResult, e.FetchOrderOK, e.FetchOrderErr
}

func (e *dummyExecutor) FetchBattle(turn uint, ID uuid.UUID) (*report.BattleReport, bool, error) {
	e.FetchBattleTurn = turn
	e.FetchBattleID = ID
	return e.FetchBattleResult, e.FetchBattleOK, e.FetchBattleErr
}

func (e *dummyExecutor) Execute(command ...handler.Command) error {
	e.CommandsExecuted = len(command)
	return nil
}

func (e *dummyExecutor) GenerateGame(races []string) (rest.StateResponse, error) {
	return rest.StateResponse{}, nil
}

func (e *dummyExecutor) GenerateTurn() (rest.StateResponse, error) {
	return rest.StateResponse{}, nil
}

func (e *dummyExecutor) BanishRace(raceName string) error {
	return nil
}

func (e *dummyExecutor) GameState() (rest.StateResponse, error) {
	return rest.StateResponse{}, nil
}

func (e *dummyExecutor) LoadReport(actor string, turn uint) (*report.Report, error) {
	return &report.Report{}, nil
}

func setupRouter() *gin.Engine {
	return setupRouterExecutor(newExecutor())
}

func setupRouterExecutor(e handler.CommandExecutor) *gin.Engine {
	return router.SetupRouter(e)
}

func newExecutor() handler.CommandExecutor {
	return &dummyExecutor{}
}

func encodeCommand(cmd any) json.RawMessage {
	v, err := json.Marshal(cmd)
	if err != nil {
		panic(err)
	}
	return v
}
