package calc_test

import (
	"math"
	"testing"

	"galaxy/calc"
)

func TestWeaponsForAttack(t *testing.T) {
	got, ok := calc.WeaponsForAttack(calc.EffectiveAttack(12, 1.5), 1.5)
	if !ok || math.Abs(got-12) > 1e-9 {
		t.Errorf("WeaponsForAttack round-trip = %v (ok=%v), want 12", got, ok)
	}
	if _, ok := calc.WeaponsForAttack(10, 0); ok {
		t.Error("WeaponsForAttack with zero tech should be infeasible")
	}
}

func TestDriveForSpeed(t *testing.T) {
	const drive, driveTech, restMass = 10.0, 1.2, 35.0
	speed := calc.Speed(calc.DriveEffective(drive, driveTech), drive+restMass)
	got, ok := calc.DriveForSpeed(speed, driveTech, restMass)
	if !ok || math.Abs(got-drive) > 1e-9 {
		t.Errorf("DriveForSpeed round-trip = %v (ok=%v), want %v", got, ok, drive)
	}
	// With a positive restMass speed can never reach 20*driveTech.
	if _, ok := calc.DriveForSpeed(20*driveTech, driveTech, restMass); ok {
		t.Error("DriveForSpeed at the speed ceiling should be infeasible")
	}
}

func TestDriveForSpeedZeroRest(t *testing.T) {
	// With restMass==0 the only achievable speed is the stripped-hull
	// ceiling 20*driveTech; any positive drive reaches it. Off-ceiling
	// targets are infeasible.
	const driveTech = 1.5
	ceiling := 20 * driveTech
	got, ok := calc.DriveForSpeed(ceiling, driveTech, 0)
	if !ok || got <= 0 {
		t.Errorf("DriveForSpeed(ceiling, _, 0) = %v (ok=%v), want positive", got, ok)
	}
	if _, ok := calc.DriveForSpeed(ceiling/2, driveTech, 0); ok {
		t.Error("DriveForSpeed(below ceiling, _, 0) should be infeasible")
	}
	if _, ok := calc.DriveForSpeed(ceiling+1, driveTech, 0); ok {
		t.Error("DriveForSpeed(above ceiling, _, 0) should be infeasible")
	}
}

func TestShieldsForDefence(t *testing.T) {
	const shields, shieldsTech, restMass = 5.75, 1.0, 40.0
	defence := calc.EffectiveDefence(shields, shieldsTech, shields+restMass)
	got, ok := calc.ShieldsForDefence(defence, shieldsTech, restMass)
	if !ok || math.Abs(got-shields) > 1e-6 {
		t.Errorf("ShieldsForDefence round-trip = %v (ok=%v), want %v", got, ok, shields)
	}
	if _, ok := calc.ShieldsForDefence(0, shieldsTech, restMass); ok {
		t.Error("ShieldsForDefence at a zero target should be infeasible")
	}
}

func TestCargoForEmptyMass(t *testing.T) {
	const restMass, cargo = 30.0, 12.0
	got, ok := calc.CargoForEmptyMass(restMass+cargo, restMass)
	if !ok || math.Abs(got-cargo) > 1e-9 {
		t.Errorf("CargoForEmptyMass round-trip = %v (ok=%v), want %v", got, ok, cargo)
	}
	if _, ok := calc.CargoForEmptyMass(restMass-1, restMass); ok {
		t.Error("CargoForEmptyMass below the fixed block mass should be infeasible")
	}
}

func TestLoadForFullMass(t *testing.T) {
	const emptyMass, cargoTech, load = 45.0, 1.0, 20.0
	full := calc.FullMass(emptyMass, calc.CarryingMass(load, cargoTech))
	got, ok := calc.LoadForFullMass(full, emptyMass, cargoTech)
	if !ok || math.Abs(got-load) > 1e-9 {
		t.Errorf("LoadForFullMass round-trip = %v (ok=%v), want %v", got, ok, load)
	}
	if _, ok := calc.LoadForFullMass(emptyMass-1, emptyMass, cargoTech); ok {
		t.Error("LoadForFullMass below empty mass should be infeasible")
	}
}
