namespace ReactorMaintenance.Simulation;

internal static class StructuralIntegritySystem
{
    public static LevelState Resolve(LevelState level)
    {
        foreach (var carrier in Enum.GetValues<ECarrierType>())
            level = ResolveCarrier(level, carrier);

        return level;
    }

    private static LevelState ResolveCarrier(LevelState level, ECarrierType carrier)
    {
        var layer = level.Layer(carrier).ToArray();
        var leaks = level.Leaks.ToList();

        foreach (var position in LevelTraversal.AllPositions(level))
        {
            var index = level.Index(position);
            var cell = layer[index];
            if (!cell.IsPresent)
                continue;

            var integrity = DegradeIntegrity(cell, carrier);
            var state = cell.State;
            if (state != EUndergroundState.Leaking && integrity <= Balancing.Current.StructuralIntegrityLeakThreshold && cell.Intensity > 0)
            {
                state = EUndergroundState.Leaking;
                leaks = UpsertLeak(leaks, level, position, carrier);
            }

            layer[index] = cell with { State = state, StructuralIntegrity = integrity };
        }

        var next = carrier switch {
            ECarrierType.Fuel => level with { Fuel = layer },
            ECarrierType.Coolant => level with { Coolant = layer },
            ECarrierType.Electricity => level with { Electricity = layer },
            _ => throw new ArgumentOutOfRangeException(nameof(carrier), carrier, "Unsupported carrier.")
        };

        return next with { Leaks = leaks.ToArray() };
    }

    private static int DegradeIntegrity(UndergroundCell cell, ECarrierType carrier)
    {
        if (cell.StructuralIntegrity >= Balancing.Current.MaxStructuralIntegrity && cell.Intensity <= Balancing.Current.StructuralPressureThreshold(carrier))
            return Balancing.Current.MaxStructuralIntegrity;

        var overPressure = Math.Max(0, cell.Intensity - Balancing.Current.StructuralPressureThreshold(carrier));
        if (overPressure <= 0 || cell.StructuralIntegrity >= Balancing.Current.MaxStructuralIntegrity)
            return Math.Clamp(cell.StructuralIntegrity, 0, Balancing.Current.MaxStructuralIntegrity);

        var damage = Math.Max(1, (int)Math.Ceiling(overPressure * Balancing.Current.StructuralIntegrityDamageScale));
        return Math.Clamp(cell.StructuralIntegrity - damage, 0, Balancing.Current.MaxStructuralIntegrity);
    }

    private static List<LeakState> UpsertLeak(List<LeakState> leaks, LevelState level, GridPosition position, ECarrierType carrier)
    {
        var accessPosition = carrier == ECarrierType.Electricity
            ? position.Neighbors().FirstOrDefault(level.IsFloor)
            : position;
        if (accessPosition is null || !level.IsFloor(accessPosition))
            return leaks;

        var index = leaks.FindIndex(leak => leak.UndergroundPosition == position && leak.Carrier == carrier);
        var leakState = new LeakState {
            Carrier = carrier,
            UndergroundPosition = position,
            AccessPosition = accessPosition,
            Repaired = false
        };

        if (index >= 0)
            leaks[index] = leakState;
        else
            leaks.Add(leakState);

        return leaks;
    }
}