package controller_test

import (
	"slices"
	"strconv"
	"testing"

	e "galaxy/error"

	"github.com/stretchr/testify/assert"
)

func TestShipClassCreate(t *testing.T) {
	c, g := newCache()

	assert.NoError(t, g.ShipClassCreate(Race_0.Name, "Random", 1, 3, 5, 4, 2))
	ships := slices.Collect(c.ListShipTypes(Race_0_idx))
	assert.Len(t, ships, 4)
	st := ships[3]
	assert.Equal(t, 1., float64(st.Drive))
	assert.Equal(t, 3, int(st.Armament))
	assert.Equal(t, 5., float64(st.Weapons))
	assert.Equal(t, 4., float64(st.Shields))
	assert.Equal(t, 2., float64(st.Cargo))

	assert.ErrorContains(t,
		g.ShipClassCreate(Race_0.Name, Race_0_Gunship, 1, 0, 0, 0, 0),
		e.GenericErrorText(e.ErrInputNewEntityDuplicateIdentifier))
	assert.ErrorContains(t,
		g.ShipClassCreate(UnknownRace, "Drone", 1, 0, 0, 0, 0),
		e.GenericErrorText(e.ErrInputUnknownRace))
	assert.ErrorContains(t,
		g.ShipClassCreate(Race_Extinct.Name, "Drone", 1, 0, 0, 0, 0),
		e.GenericErrorText(e.ErrRaceExinct))
	assert.ErrorContains(t,
		g.ShipClassCreate(Race_0.Name, BadEntityName, 1, 0, 0, 0, 0),
		e.GenericErrorText(e.ErrInputEntityTypeNameInvalid))
}

func TestCreateShipTypeValidation(t *testing.T) {
	race := Race_0.Name
	typeName := "Drone"
	type tc struct {
		name       string
		d, w, s, c float64
		a          int
		err        string
	}
	table := []tc{
		// correct values
		{typeName, 1, 0, 0, 0, 0, ""},
		{typeName, 1.1, 0, 0, 0, 0, ""},
		{typeName, 1, 1.2, 0, 0, 1, ""},
		{typeName, 1, 1.2, 2.5, 0, 1, ""},
		{typeName, 1, 0, 2.5, 7.7, 0, ""},
		// incorrect values...
		{"", 1, 0, 0, 0, 0, e.GenericErrorText(e.ErrInputEntityTypeNameInvalid)},
		{"  ", 1, 0, 0, 0, 0, e.GenericErrorText(e.ErrInputEntityTypeNameInvalid)},
		{typeName, 0, 0, 0, 0, 0, e.GenericErrorText(e.ErrInputShipTypeZeroValues)},
		// drive
		{typeName, -1, 0, 0, 0, 0, e.GenericErrorText(e.ErrInputDriveValue)},
		{typeName, 0.5, 0, 0, 0, 0, e.GenericErrorText(e.ErrInputDriveValue)},
		// weapons
		{typeName, 0, -1, 0, 0, 0, e.GenericErrorText(e.ErrInputWeaponsValue)},
		{typeName, 0, 0.5, 0, 0, 0, e.GenericErrorText(e.ErrInputWeaponsValue)},
		// shields
		{typeName, 0, 0, -1, 0, 0, e.GenericErrorText(e.ErrInputShieldsValue)},
		{typeName, 0, 0, 0.5, 0, 0, e.GenericErrorText(e.ErrInputShieldsValue)},
		// cargo
		{typeName, 0, 0, 0, -1, 0, e.GenericErrorText(e.ErrInputCargoValue)},
		{typeName, 0, 0, 0, 0.5, 0, e.GenericErrorText(e.ErrInputCargoValue)},
		// armament (and weapons)
		{typeName, 0, 0, 0, 0, -1, e.GenericErrorText(e.ErrInputShipTypeArmamentValue)},
		{typeName, 0, 1, 0, 0, 0, e.GenericErrorText(e.ErrInputShipTypeWeaponsAndArmamentValue)},
		{typeName, 0, 0, 0, 0, 1, e.GenericErrorText(e.ErrInputShipTypeWeaponsAndArmamentValue)},
	}
	for i, tc := range table {
		_, g := newCache()

		if tc.err == "" {
			err := g.ShipClassCreate(race, tc.name+strconv.Itoa(i), tc.d, tc.a, tc.w, tc.s, tc.c)
			assert.NoError(t, err)
			err = g.ShipClassCreate(race, tc.name+strconv.Itoa(i), tc.d, tc.a, tc.w, tc.s, tc.c)
			assert.ErrorContains(t, err, e.GenericErrorText(e.ErrInputNewEntityDuplicateIdentifier))
		} else {
			err := g.ShipClassCreate(race, tc.name, tc.d, tc.a, tc.w, tc.s, tc.c)
			assert.ErrorContains(t, err, tc.err)
		}
	}
}

func TestShipClassMerge(t *testing.T) {
	c, g := newCache()

	assert.Len(t, c.ShipTypes(Race_0_idx), 3)

	assert.NoError(t, g.ShipClassCreate(Race_0.Name, "Drone", 1, 0, 0, 0, 0))
	assert.Len(t, c.ShipTypes(Race_0_idx), 4)
	assert.NoError(t, g.ShipClassCreate(Race_0.Name, "Spy", 1, 0, 0, 0, 0))
	assert.Len(t, c.ShipTypes(Race_0_idx), 5)
	assert.NoError(t, g.ShipClassCreate(Race_0.Name, "Surfer", 15, 15, 15, 0, 1))
	assert.Len(t, c.ShipTypes(Race_0_idx), 6)

	assert.ErrorContains(t,
		g.ShipClassMerge(Race_0.Name, "Sky", "Drone"),
		e.GenericErrorText(e.ErrInputEntityNotExists))
	assert.ErrorContains(t,
		g.ShipClassMerge(Race_0.Name, "Spy", "Elephant"),
		e.GenericErrorText(e.ErrInputEntityNotExists))
	assert.ErrorContains(t,
		g.ShipClassMerge(Race_Extinct.Name, "Spy", "Drone"),
		e.GenericErrorText(e.ErrRaceExinct))
	assert.ErrorContains(t,
		g.ShipClassMerge(Race_0.Name, "Spy", "Spy"),
		e.GenericErrorText(e.ErrInputEntityTypeNameEquality))

	assert.NoError(t, g.ShipClassMerge(Race_0.Name, "Spy", "Drone"))
	assert.Len(t, c.ShipTypes(Race_0_idx), 5)

	assert.ErrorContains(t,
		g.ShipClassMerge(Race_0.Name, "Drone", "Surfer"),
		e.GenericErrorText(e.ErrMergeShipTypeNotEqual))
}

func TestShipClassRemove(t *testing.T) {
	c, g := newCache()

	assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Gunship, R0_Planet_0_num, 1))
	assert.NoError(t, g.ShipClassCreate(Race_0.Name, "Drone", 1, 0, 0, 0, 0))
	g.PlanetProduce(Race_0.Name, int(R0_Planet_0_num), "SHIP", "Drone")

	assert.ErrorContains(t,
		g.ShipClassRemove(UnknownRace, Race_0_Freighter),
		e.GenericErrorText(e.ErrInputUnknownRace))
	assert.ErrorContains(t,
		g.ShipClassRemove(Race_Extinct.Name, Race_0_Freighter),
		e.GenericErrorText(e.ErrRaceExinct))
	assert.ErrorContains(t,
		g.ShipClassRemove(Race_0.Name, "Elephant"),
		e.GenericErrorText(e.ErrInputEntityNotExists))
	assert.ErrorContains(t,
		g.ShipClassRemove(Race_0.Name, "Drone"),
		e.GenericErrorText(e.ErrDeleteShipTypePlanetProduction))

	assert.NoError(t, g.ShipClassRemove(Race_0.Name, Race_0_Freighter))

	assert.ErrorContains(t,
		g.ShipClassRemove(Race_0.Name, Race_0_Gunship),
		e.GenericErrorText(e.ErrDeleteShipTypeExistingGroup))
}
