namespace ReactorMaintenance.Simulation;

public sealed class SimulationEngine
{
    public LevelState MoveRobot(LevelState level, GridPosition destination)
    {
        return PlayerActionSystem.MoveRobot(level, destination);
    }

    public LevelState InteractProp(LevelState level)
    {
        return PlayerActionSystem.InteractProp(level, ResolveStep);
    }

    public LevelState InteractLeak(LevelState level, ECarrierType carrier, bool useRemedy)
    {
        return PlayerActionSystem.InteractLeak(level, carrier, useRemedy, ResolveStep);
    }

    public LevelState ApplyHeatShield(LevelState level)
    {
        return PlayerActionSystem.ApplyHeatShield(level, ResolveStep);
    }

    private LevelState ResolveStep(LevelState level)
    {
        return ResolveStep(level, true);
    }

    public LevelState ActivateReactor(LevelState level)
    {
        return ReactorSystem.Activate(level);
    }

    public LevelState EndTurn(LevelState level)
    {
        return ResolveStep(level);
    }

    public LevelState AdvanceTurn(LevelState level)
    {
        return ResolveStep(level);
    }

    public IReadOnlyList<Forecast> Forecast(LevelState level)
    {
        return ForecastSystem.Forecast(level, simulated => ResolveStep(simulated, false));
    }

    private LevelState ResolveStep(LevelState level, bool refreshForecasts)
    {
        var report = m_Validator.Validate(level);
        if (!report.IsValid)
            return level with { Global = level.Global with { LevelState = ELevelState.Lost, Status = report.Errors[0].Message } };

        var next = level;
        next = NetworkPropagationSystem.Propagate(next);
        next = ConsumerSystem.Resolve(next);
        next = StructuralIntegritySystem.Resolve(next);
        next = LeakSystem.Inject(next);
        next = SurfaceInteractionSystem.Resolve(next);
        next = RobotSafetySystem.Resolve(next);
        next = ReactorSystem.DeriveState(next);
        next = SurfaceInteractionSystem.AdvanceDurations(next);
        next = next with {
            Global = next.Global with {
                Turn = next.Global.Turn + 1
            }
        };

        return refreshForecasts ? next with { Forecasts = Forecast(next) } : next;
    }

    private readonly LevelValidator m_Validator = new();
}