namespace splat;

using Godot;

/// <summary>
/// A placed building. Observes its own <see cref="BuildingData"/> structural
/// integrity and applies physical consequences (leaning, deformation, collapse)
/// as integrity drops.
/// </summary>
public partial class Building : StaticBody3D
{
    [Export] public BuildingData Data { get; set; } = default!;

    public float Integrity => Data?.StructuralIntegrity ?? 0f;

    /// <summary>True once the structure has fully collapsed into debris.</summary>
    public bool HasCollapsed { get; private set; }

    /// <summary>Emitted when this building collapses (integrity reached 0).</summary>
    [Signal] public delegate void CollapsedEventHandler(Building building);

    private float _leanAmount;

    public override void _Ready()
    {
        CollisionLayer = 1u; // Buildings physics layer

        if (Data != null && Data.Mass <= 0f)
            Data.Init();

        // Default visual + collision so a placed building is tangible even
        // before a modelled mesh is assigned (KayKit assets are added later).
        if (GetNodeOrNull<CollisionShape3D>("CollisionShape3D") == null)
        {
            var shape = new CollisionShape3D { Name = "CollisionShape3D" };
            shape.Shape = new BoxShape3D { Size = new Vector3(4f, 4f, 4f) };
            AddChild(shape);
        }

        if (GetNodeOrNull<MeshInstance3D>("MeshInstance3D") == null)
        {
            var mesh = new MeshInstance3D { Name = "MeshInstance3D" };
            var box = new BoxMesh { Size = new Vector3(4f, 4f, 4f) };
            mesh.Mesh = box;
            mesh.Position = new Vector3(0f, 2f, 0f);
            AddChild(mesh);
        }

        ApplyDecayVisuals();
    }

    /// <summary>
    /// Apply damage. Integrity is clamped to [0, 1]; at 0 the building collapses.
    /// </summary>
    public void ApplyDamage(float amount)
    {
        if (HasCollapsed || Data == null)
            return;

        Data.StructuralIntegrity = Mathf.Clamp(Data.StructuralIntegrity - amount, 0f, 1f);
        ApplyDecayVisuals();

        if (Data.StructuralIntegrity <= 0f)
            Collapse();
    }

    /// <summary>Restore integrity via maintenance/repair.</summary>
    public void Repair(float amount)
    {
        if (HasCollapsed || Data == null)
            return;
        Data.StructuralIntegrity = Mathf.Clamp(Data.StructuralIntegrity + amount, 0f, 1f);
        ApplyDecayVisuals();
    }

    /// <summary>
    /// Map integrity onto visible lean/tilt. Poorly supported structures lean
    /// (rotation applied based on torque imbalance), more so as integrity falls.
    /// </summary>
    private void ApplyDecayVisuals()
    {
        if (HasCollapsed || Data == null)
            return;

        var t = 1f - Data.StructuralIntegrity;
        // Deterministic lean direction from instance id so each building leans uniquely.
        var dir = (int)(GetInstanceId() % 4);
        _leanAmount = t * 0.25f;
        var axis = dir switch
        {
            0 => Vector3.Forward,
            1 => Vector3.Right,
            2 => Vector3.Back,
            _ => Vector3.Left
        };
        Rotation = new Vector3(axis.X, 0f, axis.Z) * _leanAmount;
    }

    private void Collapse()
    {
        HasCollapsed = true;
        EmitSignal(SignalName.Collapsed, this);
        // TODO: Replace the intact body with RigidBody3D debris and propagate
        // impact forces to neighbouring structures once structural physics lands.
        QueueFree();
    }
}
