package router_test

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"net/http/httptest"
	"testing"

	"galaxy/model/report"

	"github.com/google/uuid"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestGetBattleValidation(t *testing.T) {
	validUUID := uuid.New().String()

	for _, tc := range []struct {
		description  string
		turn         string
		battleID     string
		expectStatus int
	}{
		{"Negative turn", "-1", validUUID, http.StatusBadRequest},
		{"Non-numeric turn", "abc", validUUID, http.StatusBadRequest},
		{"Invalid uuid", "0", invalidId, http.StatusBadRequest},
	} {
		t.Run(tc.description, func(t *testing.T) {
			e := &dummyExecutor{}
			r := setupRouterExecutor(e)

			w := httptest.NewRecorder()
			path := fmt.Sprintf("/api/v1/battle/%s/%s", tc.turn, tc.battleID)
			req, _ := http.NewRequest(http.MethodGet, path, nil)
			r.ServeHTTP(w, req)

			assert.Equal(t, tc.expectStatus, w.Code, w.Body)
			assert.Equal(t, uuid.Nil, e.FetchBattleID, "FetchBattle must not be called on validation error")
		})
	}
}

func TestGetBattleFound(t *testing.T) {
	id := uuid.New()
	raceA := uuid.New()
	raceB := uuid.New()
	stored := &report.BattleReport{
		ID:         id,
		Planet:     42,
		PlanetName: "X-Prime",
		Races: map[int]uuid.UUID{
			0: raceA,
			1: raceB,
		},
		Ships: map[int]report.BattleReportGroup{
			10: {
				Race:         "Alpha",
				ClassName:    "Drone",
				Tech:         map[string]report.Float{"WEAPONS": report.F(1)},
				Number:       5,
				NumberLeft:   3,
				LoadType:     "EMP",
				LoadQuantity: report.F(0),
				InBattle:     true,
			},
			20: {
				Race:         "Beta",
				ClassName:    "Spy",
				Tech:         map[string]report.Float{"SHIELDS": report.F(2)},
				Number:       4,
				NumberLeft:   0,
				LoadType:     "EMP",
				LoadQuantity: report.F(0),
				InBattle:     true,
			},
		},
		Protocol: []report.BattleActionReport{
			{Attacker: 0, AttackerShipClass: 10, Defender: 1, DefenderShipClass: 20, Destroyed: true},
		},
	}
	e := &dummyExecutor{
		FetchBattleResult: stored,
		FetchBattleOK:     true,
	}
	r := setupRouterExecutor(e)

	w := httptest.NewRecorder()
	path := fmt.Sprintf("/api/v1/battle/%d/%s", 7, id.String())
	req, _ := http.NewRequest(http.MethodGet, path, nil)
	r.ServeHTTP(w, req)

	require.Equal(t, http.StatusOK, w.Code, w.Body)
	assert.Equal(t, uint(7), e.FetchBattleTurn)
	assert.Equal(t, id, e.FetchBattleID)

	var got report.BattleReport
	require.NoError(t, json.Unmarshal(w.Body.Bytes(), &got))
	assert.Equal(t, stored.ID, got.ID)
	assert.Equal(t, stored.Planet, got.Planet)
	assert.Equal(t, stored.PlanetName, got.PlanetName)
	assert.Equal(t, stored.Races, got.Races)
	require.Len(t, got.Ships, len(stored.Ships))
	assert.Equal(t, stored.Ships[10].ClassName, got.Ships[10].ClassName)
	assert.Equal(t, stored.Ships[20].NumberLeft, got.Ships[20].NumberLeft)
	require.Len(t, got.Protocol, 1)
	assert.Equal(t, stored.Protocol[0], got.Protocol[0])
}

func TestGetBattleTurnZero(t *testing.T) {
	id := uuid.New()
	e := &dummyExecutor{
		FetchBattleResult: &report.BattleReport{ID: id},
		FetchBattleOK:     true,
	}
	r := setupRouterExecutor(e)

	w := httptest.NewRecorder()
	req, _ := http.NewRequest(http.MethodGet, fmt.Sprintf("/api/v1/battle/0/%s", id.String()), nil)
	r.ServeHTTP(w, req)

	require.Equal(t, http.StatusOK, w.Code, w.Body)
	assert.Equal(t, uint(0), e.FetchBattleTurn)
	assert.Equal(t, id, e.FetchBattleID)
}

func TestGetBattleNotFound(t *testing.T) {
	id := uuid.New()
	e := &dummyExecutor{FetchBattleOK: false}
	r := setupRouterExecutor(e)

	w := httptest.NewRecorder()
	req, _ := http.NewRequest(http.MethodGet, fmt.Sprintf("/api/v1/battle/3/%s", id.String()), nil)
	r.ServeHTTP(w, req)

	assert.Equal(t, http.StatusNotFound, w.Code, w.Body)
	assert.Equal(t, uint(3), e.FetchBattleTurn)
	assert.Equal(t, id, e.FetchBattleID)
}

func TestGetBattleEngineError(t *testing.T) {
	e := &dummyExecutor{FetchBattleErr: errors.New("engine boom")}
	r := setupRouterExecutor(e)

	w := httptest.NewRecorder()
	req, _ := http.NewRequest(http.MethodGet, fmt.Sprintf("/api/v1/battle/3/%s", uuid.NewString()), nil)
	r.ServeHTTP(w, req)

	assert.Equal(t, http.StatusInternalServerError, w.Code, w.Body)
}
