namespace splat;

using System.Threading.Tasks;
using Chickensoft.GoDotTest;
using Chickensoft.GodotTestDriver;
using Godot;
using Shouldly;

public class BuildingTest : TestClass
{
    private Fixture _fixture = default!;
    private Game _game = default!;

    public BuildingTest(Node testScene) : base(testScene) { }

    [SetupAll]
    public async Task Setup()
    {
        _fixture = new Fixture(TestScene.GetTree());
        _game = await _fixture.LoadAndAddScene<Game>();
    }

    [CleanupAll]
    public void Cleanup() => _fixture.Cleanup();

    [Test]
    public void MaterialDensitiesMatchPhysicalReferenceValues()
    {
        BuildingData.Density(MaterialType.Stone).ShouldBe(2400f);
        BuildingData.Density(MaterialType.Timber).ShouldBe(600f);
        BuildingData.Density(MaterialType.Turf).ShouldBe(400f);
        BuildingData.Density(MaterialType.Concrete).ShouldBe(2400f);
        BuildingData.Density(MaterialType.Steel).ShouldBe(7850f);
    }

    [Test]
    public void InitialisationComputesMissingMassButPreservesAuthoredMass()
    {
        var computed = new BuildingData { Material = MaterialType.Timber, Volume = 10f };
        computed.Init();
        computed.Mass.ShouldBe(6000f);

        var authored = new BuildingData { Material = MaterialType.Stone, Volume = 10f, Mass = 1234f };
        authored.Init();
        authored.Mass.ShouldBe(1234f);
    }

    [Test]
    public void DamageAndRepairClampIntegrityAndUpdateLean()
    {
        var building = CreateBuilding();
        building.ApplyDamage(0.4f);
        building.Integrity.ShouldBe(0.6f, tolerance: 0.001f);
        building.Rotation.Length().ShouldBeGreaterThan(0f);

        building.Repair(2f);
        building.Integrity.ShouldBe(1f);
        building.Rotation.Length().ShouldBe(0f, tolerance: 0.001f);
        building.Free();
    }

    [Test]
    public void CollapseSignalsAndUnregistersFromManager()
    {
        var building = CreateBuilding();
        var buildingSignal = false;
        var managerSignal = false;
        building.Collapsed += _ => buildingSignal = true;
        _game.Buildings.BuildingCollapsed += collapsed => managerSignal = collapsed == building;
        _game.Buildings.Register(building);

        building.ApplyDamage(1f);

        building.HasCollapsed.ShouldBeTrue();
        buildingSignal.ShouldBeTrue();
        managerSignal.ShouldBeTrue();
        _game.Buildings.Buildings.ShouldNotContain(building);
    }

    [Test]
    public void NewDayAppliesEachBuildingsAuthoredDecayRate()
    {
        var building = CreateBuilding();
        building.Data.DecayRate = 0.125f;
        _game.Buildings.Register(building);

        _game.Manager.EmitSignal(GameManager.SignalName.NewDay, 1);

        building.Integrity.ShouldBe(0.875f, tolerance: 0.001f);
        _game.Buildings.Unregister(building);
        building.Free();
    }

    [Test]
    public void DuplicateRegistrationIsIgnored()
    {
        var building = CreateBuilding();
        var before = _game.Buildings.Buildings.Count;

        _game.Buildings.Register(building);
        _game.Buildings.Register(building);

        _game.Buildings.Buildings.Count.ShouldBe(before + 1);
        _game.Buildings.Unregister(building);
        building.Free();
    }

    [Test]
    public void UnregisterDisconnectsCollapseNotifications()
    {
        var building = CreateBuilding();
        var managerSignals = 0;
        _game.Buildings.BuildingCollapsed += _ => managerSignals++;
        _game.Buildings.Register(building);
        _game.Buildings.Unregister(building);

        building.ApplyDamage(1f);

        managerSignals.ShouldBe(0);
    }

    private Building CreateBuilding()
    {
        var building = _game.BuildingScene.Instantiate<Building>();
        building.Data = (BuildingData)building.Data.Duplicate(true);
        TestScene.AddChild(building);
        return building;
    }
}
