package calc_test

import (
	"math"
	"testing"

	"galaxy/calc"
)

func TestShipBuildCost(t *testing.T) {
	cases := []struct {
		name      string
		shipMass  float64
		material  float64
		resources float64
		want      float64
	}{
		{
			name:      "material exceeds mass: no farming needed",
			shipMass:  5,
			material:  10,
			resources: 0.5,
			want:      50, // ShipProductionCost(5) = 50; matFarm = 0.
		},
		{
			name:      "material equal to mass: no farming needed",
			shipMass:  5,
			material:  5,
			resources: 0.5,
			want:      50,
		},
		{
			name:      "material short of mass: farming term added",
			shipMass:  10,
			material:  3,
			resources: 0.5,
			want:      114, // 100 + (7 / 0.5).
		},
		{
			name:      "no material at all: full mass farmed",
			shipMass:  4,
			material:  0,
			resources: 0.5,
			want:      48, // 40 + (4 / 0.5).
		},
		{
			name:      "zero resources collapses farming term to zero",
			shipMass:  10,
			material:  3,
			resources: 0,
			want:      100, // 100 + 0; resources == 0 is a pathological guard.
		},
	}
	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			got := calc.ShipBuildCost(tc.shipMass, tc.material, tc.resources)
			if math.Abs(got-tc.want) > 1e-9 {
				t.Errorf("ShipBuildCost(%v, %v, %v) = %v, want %v",
					tc.shipMass, tc.material, tc.resources, got, tc.want)
			}
		})
	}
}

func TestProduceShipsInTurn(t *testing.T) {
	cases := []struct {
		name                                               string
		productionAvailable, material, resources, shipMass float64
		wantShips                                          uint
		wantMaterialLeft, wantProductionUsed, wantProgress float64
	}{
		{
			name:                "ample material: ten ships, no farming",
			productionAvailable: 100, material: 100, resources: 10, shipMass: 1,
			wantShips: 10, wantMaterialLeft: 90, wantProductionUsed: 0, wantProgress: 0,
		},
		{
			name:                "no material: partial progress on a farmed ship",
			productionAvailable: 114, material: 0, resources: 0.5, shipMass: 10,
			// ShipBuildCost(10,0,0.5) = 100 + 10/0.5 = 120; 114/120 = 0.95.
			wantShips: 0, wantMaterialLeft: 0, wantProductionUsed: 114, wantProgress: 0.95,
		},
		{
			name:                "no production available leaves the stockpile",
			productionAvailable: 0, material: 50, resources: 10, shipMass: 5,
			wantShips: 0, wantMaterialLeft: 50, wantProductionUsed: 0, wantProgress: 0,
		},
		{
			name:                "zero ship mass is guarded against an endless loop",
			productionAvailable: 100, material: 50, resources: 10, shipMass: 0,
			wantShips: 0, wantMaterialLeft: 50, wantProductionUsed: 0, wantProgress: 0,
		},
	}
	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			ships, materialLeft, productionUsed, progress := calc.ProduceShipsInTurn(
				tc.productionAvailable, tc.material, tc.resources, tc.shipMass)
			if ships != tc.wantShips {
				t.Errorf("ships = %d, want %d", ships, tc.wantShips)
			}
			if math.Abs(materialLeft-tc.wantMaterialLeft) > 1e-9 {
				t.Errorf("materialLeft = %v, want %v", materialLeft, tc.wantMaterialLeft)
			}
			if math.Abs(productionUsed-tc.wantProductionUsed) > 1e-9 {
				t.Errorf("productionUsed = %v, want %v", productionUsed, tc.wantProductionUsed)
			}
			if math.Abs(progress-tc.wantProgress) > 1e-9 {
				t.Errorf("progress = %v, want %v", progress, tc.wantProgress)
			}
		})
	}
}
